# CLI
Source: https://docs.orbitflare.com/agents/cli
Use the OrbitFlare CLI in shell scripts, CI/CD pipelines, and agent-driven automation. Every command supports --json for machine-readable output.
The [OrbitFlare CLI](/cli) is a single binary that wraps RPC queries, gRPC and Jetstream streaming with YAML configs, project scaffolding, and account management. For agent and automation workflows, the key feature is `--json`: every command emits structured output that composes cleanly with `jq`, shell pipes, GitHub Actions, and cron jobs.
For interactive AI workflows in Claude Code, Cursor, or other MCP hosts, prefer the [MCP server](/agents/mcp). The CLI is best for shell-driven automation where you want deterministic exit codes and parseable output.
## Install
```bash theme={null}
cargo install orbitflare # requires Rust 1.85+
orbitflare --version
```
See the [full CLI reference](/cli) for installation alternatives, auth methods, and the complete command surface.
## Headless setup
For non-interactive environments (CI, containers, scripts), authenticate with an API key directly, no browser flow needed:
```bash theme={null}
orbitflare auth login --x-orbit-key "$ORBITFLARE_API_KEY"
orbitflare config set rpc.url "$ORBITFLARE_RPC_URL"
orbitflare config set grpc.url "$ORBITFLARE_GRPC_URL"
orbitflare config set jetstream.url "$ORBITFLARE_JETSTREAM_URL"
orbitflare ping --json
```
Credentials are stored in the OS keychain by default. In containers without a keychain, set `ORBITFLARE_CONFIG_DIR` to a writable path and the CLI will fall back to a file-based config.
## JSON output
Every command supports `--json`:
```bash theme={null}
$ orbitflare rpc slot --json
{"slot": 312456789}
$ orbitflare rpc balance Gh9ZwEm... --json
{"address": "Gh9ZwEm...", "lamports": 12500000000, "sol": 12.5}
```
Compose with `jq` for one-liners:
```bash theme={null}
# Print just the slot
orbitflare rpc slot --json | jq -r '.slot'
# Alert if balance drops below 1 SOL
SOL=$(orbitflare rpc balance "$WALLET" --json | jq -r '.sol')
if (( $(echo "$SOL < 1" | bc -l) )); then
echo "Balance low: $SOL SOL"
exit 1
fi
```
## Streaming with YAML configs
The CLI streams Jetstream and Yellowstone gRPC using a declarative YAML config, no code required:
```yaml theme={null}
# pump-trades.yaml
transactions:
pump_trades:
account_include:
- "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
reconnect:
initial_delay_ms: 100
max_delay_ms: 30000
multiplier: 2.0
max_retries: 0
```
Run it (Jetstream or Yellowstone gRPC):
```bash theme={null}
orbitflare jet --config pump-trades.yaml | your-indexer
orbitflare grpc --config pump-trades.yaml | your-indexer
```
Have an MCP-equipped agent generate the YAML for you. The [MCP server](/agents/mcp) `subscribeTransactions` tool emits a CLI-ready config from a natural-language description.
## Scripting patterns
### Tail recent signatures and process them
```bash theme={null}
orbitflare rpc history "$WALLET" --limit 50 --json \
| jq -r '.[].signature' \
| while read sig; do
orbitflare rpc tx "$sig" --json > "txs/$sig.json"
done
```
### CI health check
```yaml theme={null}
# .github/workflows/rpc-health.yml
name: OrbitFlare RPC health
on:
schedule:
- cron: '*/15 * * * *'
jobs:
ping:
runs-on: ubuntu-latest
steps:
- uses: actions-rs/toolchain@v1
with: { toolchain: stable }
- run: cargo install orbitflare
- run: orbitflare auth login --x-orbit-key ${{ secrets.ORBITFLARE_API_KEY }}
- run: orbitflare config set rpc.url ${{ secrets.ORBITFLARE_RPC_URL }}
- run: orbitflare ping --json
```
### Scaffold a project from an agent
The CLI ships templates for common Solana projects. An agent can list them and spin one up in one shot:
```bash theme={null}
orbitflare template --list --json
orbitflare template --install solana-copy-trader --dir ./bot
```
## Profiles
Switch between accounts or environments without re-authenticating each time:
```bash theme={null}
orbitflare auth login --profile prod --x-orbit-key "$PROD_KEY"
orbitflare auth login --profile dev --x-orbit-key "$DEV_KEY"
orbitflare auth switch --profile prod
orbitflare auth status --json
```
## Source
* CLI repo: [github.com/orbitflare/orbit-cli](https://github.com/orbitflare/orbit-cli)
* Full reference: [CLI documentation](/cli)
# MCP Server
Source: https://docs.orbitflare.com/agents/mcp
53 OrbitFlare tools exposed via the Model Context Protocol. Works in Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Gemini CLI, and Codex CLI.
The OrbitFlare MCP server exposes Shredstream-optimized Solana RPC, gRPC streaming, dedicated nodes, and validator infrastructure as tools your AI assistant can call directly. Each capability is its own tool with a focused input schema: no internal action routers, no result-id pagination dance.
Pair the MCP server with the [Skill](/agents/skills) for the best experience. The skill teaches the agent *how* OrbitFlare works; the MCP server lets it actually call OrbitFlare without leaving the chat.
## Install
Most hosts take the same JSON shape; only the file location differs:
```json theme={null}
{
"mcpServers": {
"orbitflare": {
"command": "npx",
"args": ["@orbitflare/mcp@latest"],
"env": {
"ORBITFLARE_API_KEY": "your-license-key",
"ORBITFLARE_NETWORK": "mainnet"
}
}
}
}
```
| Host | Where to put it |
| - | - |
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) · `%APPDATA%\Claude\claude_desktop_config.json` (Windows) |
| Claude Code | run `claude mcp add orbitflare -- npx @orbitflare/mcp@latest`, or drop a `.mcp.json` at your project root |
| Cursor | `~/.cursor/mcp.json` (global) or `/.cursor/mcp.json` |
| Gemini CLI | `~/.gemini/settings.json` (global) or `/.gemini/settings.json` |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
| VS Code | depends on extension (Continue, Cline, GitHub Copilot agent mode) |
**Codex CLI** uses TOML at `~/.codex/config.toml` (or `/.codex/config.toml`):
```toml theme={null}
[mcp_servers.orbitflare]
command = "npx"
args = ["@orbitflare/mcp@latest"]
```
### Get a license key
Sign up at [orbitflare.com](https://orbitflare.com) and copy your license key from the **Licenses** section of the dashboard. The Free plan gives you 10 RPS, enough to start building immediately.
If you'd rather not put the key in the host config, leave the env var unset and call `setApiKey` from chat:
> Set my OrbitFlare API key to `xxxx-xxxx-xxxx`.
## Configuration
| Env var | Purpose |
| - | - |
| `ORBITFLARE_API_KEY` | RPC license key (used as `?api_key=` for JSON-RPC) |
| `ORBITFLARE_LICENSE_KEY` | Alias for `ORBITFLARE_API_KEY` |
| `ORBITFLARE_CUSTOMER_API_KEY` | Customer API v2 key (`X-ORBIT-KEY` header). Falls back to `ORBITFLARE_API_KEY` if unset |
| `ORBITFLARE_NETWORK` | `mainnet` (default) or `devnet` |
| `ORBITFLARE_RPC_URL` | Optional explicit RPC endpoint URL (overrides region/network). Useful for dedicated nodes. |
| `ORBITFLARE_BEARER_TOKEN` | Bearer token for the Customer API. Auto-populated by `pollDeviceToken` / `verifyWalletSignature` and persisted at `~/.orbitflare/mcp-session.json`. |
All values can be overridden at runtime via the `setApiKey` tool, which is handy when switching networks or pinning a region without restarting the host.
### Regional endpoints
`mainnet` is the default network. Endpoints are region-pinned; pin one by calling `setApiKey` with a region code: `fra`, `ams`, `lon`, `ny`, `la`, `slc`, `sg`, `va`, `jp`.
## Tools
53 tools, grouped by domain. Every tool is independently callable.
### RPC reads
| Tool | What it does |
| - | - |
| `getBalance` | SOL balance for a pubkey, in lamports and SOL |
| `getAccountInfo` | Full account info with `jsonParsed` decoding when available |
| `getMultipleAccounts` | Batch up to 100 accounts in one call |
| `getProgramAccounts` | Accounts owned by a program (filters + dataSlice required for popular programs) |
| `getTokenAccountsByOwner` | All SPL token accounts a wallet owns |
| `getTokenAccountBalance` | Balance of a specific SPL token account |
| `getTokenSupply` | Total supply for an SPL mint |
| `getTransaction` | Parsed transaction by signature |
| `getSignaturesForAddress` | Recent signatures (paginated via `before` / `until`) |
| `getBlock` | Block metadata + transactions at a slot |
| `getSlot` | Current slot |
| `getEpochInfo` | Epoch number, slot index, slots-in-epoch, progress |
| `getNetworkStatus` | Health + version + identity + slot in one call |
| `getLatestBlockhash` | Recent blockhash + lastValidBlockHeight |
| `getFeeForMessage` | Lamport fee for a compiled message |
| `getMinimumBalanceForRentExemption` | Min lamports for rent exemption |
| `getStakeActivation` | Active/inactive lamports + state for a stake account |
| `getRecentPrioritizationFees` | Priority-fee samples + P50/P75/P90/P99 percentiles |
| `simulateTransaction` | Simulate a tx; returns logs + units consumed |
| `sendTransaction` | Submit an already-signed base-64 transaction |
### Account, profile, and plans
| Tool | What it does |
| - | - |
| `setApiKey` | Store an RPC or Customer API key for the session, optionally switch network/region |
| `getProfile` | Authenticated user profile + account balance |
| `updateProfile` | Update name, email, payment wallet |
| `listLicenses` | All RPC licenses on the account |
| `listRpcPlans` | Catalog of available plans with pricing |
### Auth (device code + wallet challenge)
| Tool | What it does |
| - | - |
| `requestDeviceCode` | Start device-authorization flow; returns `verification_uri` + `user_code` |
| `pollDeviceToken` | Poll for completion; auto-stores Bearer token on success |
| `revokeToken` | Logout / clear Bearer token |
| `requestWalletChallenge` | Get a one-time challenge string for a Solana wallet |
| `verifyWalletSignature` | Sign in an existing account by signature |
| `registerWithWallet` | Create a new account from a wallet signature |
### API keys and licenses
| Tool | What it does |
| - | - |
| `listApiKeys` | List all API keys (masked) |
| `createApiKey` | Create a new API key (full key returned **once**) |
| `regenerateApiKey` | Regenerate by id (old key invalidated immediately) |
| `deleteApiKey` | Permanently delete an API key |
| `addLicenseIp` | Whitelist an IP on an IP-mode license |
| `removeLicenseIp` | Remove a whitelisted IP |
| `regenerateLicenseKey` | Rotate a license's RPC key |
### Billing
| Tool | What it does |
| - | - |
| `prepareTopUp` | Build an unsigned USDC top-up transaction |
| `confirmTopUp` | Submit the signed top-up; OrbitFlare verifies on-chain and credits balance |
| `listInvoices` | Paginated invoice list |
| `purchaseRpcPlan` | Purchase or `validateOnly`-dry-run a plan from your account balance |
### Streaming, staking, swap
| Tool | What it does |
| - | - |
| `subscribeTransactions` | Build a ready-to-use Jetstream or Yellowstone gRPC config for the OrbitFlare CLI |
| `getStakeInfo` | OrbitFlare validator identity, vote account, commission, est. APY, optional reward calculator |
| `getSwapQuote` | Jupiter Metis routing quote (auto-resolves `SOL` / `USDC` / `USDT` symbols) |
### Wallet and write
| Tool | What it does |
| - | - |
| `generateKeypair` | Generate Ed25519 keypair, optionally persist to `~/.orbitflare/mcp-keypair.json` |
| `loadKeypair` | Load from base58 secret or Solana CLI keypair file |
| `clearKeypair` | Remove from session (and optionally disk) |
| `getWalletAddress` | Returns the loaded wallet's address |
| `signMessage` | Ed25519-sign an arbitrary message (utf8 / base58 / base64) |
| `buildSolTransfer` | Build an unsigned versioned tx transferring SOL |
| `buildStakeDelegation` | Build an unsigned tx delegating to OrbitFlare's vote account (or any vote account) |
| `signAndSendTransaction` | Sign with the session keypair, broadcast via OrbitFlare RPC |
## Flows
### Auth (device code)
`requestDeviceCode` returns `verification_uri` + `user_code`. The model shows them to you.
`pollDeviceToken { device_code }` repeats every `interval` seconds until success. The Bearer token is stored automatically and used for subsequent Customer API calls.
`revokeToken` clears the Bearer token.
### Auth (wallet)
`generateKeypair` (creates fresh) or `loadKeypair` (from base58 / Solana CLI file).
`requestWalletChallenge { wallet_address }`.
`signMessage { message: }` then `verifyWalletSignature` (existing account) or `registerWithWallet` (new account).
### Wallet send
`generateKeypair` or `loadKeypair`.
`buildSolTransfer { recipient, amountSol }` returns a base64 unsigned tx.
`signAndSendTransaction { transaction }` signs with the session keypair, broadcasts, and returns signature + Solscan link.
### Top-up
`prepareTopUp { amount, wallet_address }` returns a base64 unsigned USDC transfer + memo.
Sign with the loaded wallet via `signAndSendTransaction`, or sign externally.
`confirmTopUp { transaction: }`. OrbitFlare verifies on-chain and credits your balance.
## Resources
The server exposes OrbitFlare's documentation as MCP resources, so the model can pull current docs on demand:
| URI | Page |
| - | - |
| `orbitflare://docs/index` | Documentation index (`llms.txt`) |
| `orbitflare://docs/quickstart` | Quickstart |
| `orbitflare://docs/authentication` | Auth, endpoints, rate limits, connection limits |
| `orbitflare://docs/products` | Products & pricing |
| `orbitflare://docs/rpc-http` | HTTP RPC reference |
| `orbitflare://docs/jetstream` | Jetstream streaming |
| `orbitflare://docs/yellowstone` | Yellowstone gRPC |
| `orbitflare://docs/staking` | Validator staking |
| `orbitflare://docs/metis-swap` | Metis Swap API |
| `orbitflare://docs/cli` | OrbitFlare CLI |
| `orbitflare://docs/customer-api` | Customer API v2 |
## System prompts
Pre-built system / developer prompts ship in the [`system-prompts/orbitflare/`](https://github.com/orbitflare/orbitflare-mcp/tree/main/system-prompts/orbitflare) directory:
* `claude.system.md`: concise system prompt for Claude
* `openai.developer.md`: concise developer message for OpenAI models
* `full.md`: long-form operator playbook (auth flows, top-up, hard guardrails)
Drop one into your host config to give the model end-to-end guidance on when to reach for which tool.
## Try it
Once installed, the model picks the right tool automatically:
> "What is the SOL balance of `Gh9ZwEm…`?"
> "Show me the last 20 transactions for `7xKXt…`."
> "What's a competitive priority fee right now for an account that touches Raydium?"
> "Build me a Yellowstone gRPC config that streams Jupiter swap transactions from Frankfurt."
> "Quote 1 SOL → USDC with 0.3% slippage."
> "What is OrbitFlare's validator vote account, and what would 250 SOL earn per year?"
## Source
* MCP server: [github.com/orbitflare/orbitflare-mcp](https://github.com/orbitflare/orbitflare-mcp)
* npm: [`@orbitflare/mcp`](https://www.npmjs.com/package/@orbitflare/mcp)
# AI Agents on OrbitFlare
Source: https://docs.orbitflare.com/agents/overview
Build AI agents on OrbitFlare. Skills, MCP server, CLI, and SDK: everything an agent needs to query Solana, stream data, and manage an OrbitFlare account.
OrbitFlare ships a complete agent surface for Solana. Drop a skill into Claude Code or Cursor, wire up the MCP server in any compatible host, script the CLI from a shell, or call the Rust SDK directly. All four talk to the same Shredstream-optimized RPC, gRPC streaming, and Customer API.
**Start with the MCP server.** It exposes 53 typed tools that cover RPC reads, streaming config, account management, billing, swaps, and on-chain wallet operations, and it works in Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Gemini CLI, and Codex CLI out of the box.
## What's available
Drop-in instruction sets that teach Claude Code, Cursor, and Codex how to use OrbitFlare correctly: RPC, streaming, dedicated nodes, trading APIs, CLI, and SDK in one skill.
53 tools across RPC, streaming, billing, swaps, staking, and wallet ops. Works in any MCP-compatible host. Includes auth flows, top-up, and live docs as resources.
Single binary for streaming, querying, scaffolding, and account management. Every command supports `--json` so output is parseable in shell scripts and CI/CD.
Typed Rust clients for RPC, WebSocket, Yellowstone gRPC, and Jetstream. Built-in retry, failover, and reconnection. Reads endpoints from environment variables.
## Skill vs MCP vs CLI vs SDK
Pick the surface that matches the workflow:
| Surface | Best for | When to reach for it |
| - | - | - |
| **Skill** | Coding agents (Claude Code, Cursor, Codex) writing OrbitFlare code | You want the agent to **write** correct OrbitFlare code (endpoint URLs, gRPC filters, SDK setup) without you fetching docs first |
| **MCP** | Agents that need to **call** OrbitFlare directly from chat | You want the agent to query balances, send txs, build streaming configs, or manage your account interactively |
| **CLI** | Shell scripts, CI/CD, terminal automation | You're writing bash, GitHub Actions, or cron jobs, and `--json` output composes cleanly with `jq` and shell pipes |
| **SDK** | Production Rust services | You're building a long-running daemon, indexer, or trading bot and want type-safe access with retry/failover baked in |
Skills and MCP are complementary. Install both: the skill teaches the agent *how* OrbitFlare works; the MCP server lets it actually call OrbitFlare without leaving the chat.
## Recommended setup
The fastest path for a coding agent on Solana:
Sign up at [orbitflare.com](https://orbitflare.com) and copy your license key from the **Licenses** section. The Free plan gives you 10 RPS / 1 TPS forever.
```bash theme={null}
npx @orbitflare/skills@latest # Claude Code (personal)
```
See [Skills](/agents/skills) for Cursor, Codex, and project-scoped installs.
Add to your host's MCP config:
```json theme={null}
{
"mcpServers": {
"orbitflare": {
"command": "npx",
"args": ["@orbitflare/mcp@latest"],
"env": {
"ORBITFLARE_API_KEY": "your-license-key",
"ORBITFLARE_NETWORK": "mainnet"
}
}
}
}
```
See [MCP Server](/agents/mcp) for per-host file paths and the Codex CLI TOML variant.
Restart Claude Code (or your host of choice) so it picks up the new skill and MCP server.
> "Stream pump.fun trades using OrbitFlare Jetstream from Frankfurt"
> "What is the SOL balance of `Gh9ZwEm…`? Then show me the last 20 transactions for that address."
> "Quote 1 SOL → USDC with 0.3% slippage and build me a Yellowstone config to watch the pool."
## Common workflows
| Goal | Surfaces to use |
| - | - |
| Build a trading bot | **MCP** for prototyping in chat → **Rust SDK** for the production daemon |
| Stream real-time data | **MCP** `subscribeTransactions` to generate the YAML config → **CLI** `orbitflare stream` to run it |
| Index historical data | **Skill** to learn `getTransactionsForAddress` and the archive endpoint → **SDK** for the indexer |
| Manage account / billing | **MCP** auth flows + `prepareTopUp` / `confirmTopUp` from chat |
| Quote a swap | **MCP** `getSwapQuote` (Jupiter Metis routing, auto-resolves SOL/USDC/USDT symbols) |
| CI / scripted automation | **CLI** with `--json` flag piped into `jq` |
## Source code
Everything is open source:
* Skills: [github.com/orbitflare/orbitflare-skills](https://github.com/orbitflare/orbitflare-skills)
* MCP server: [github.com/orbitflare/orbitflare-mcp](https://github.com/orbitflare/orbitflare-mcp)
* CLI: [github.com/orbitflare/orbit-cli](https://github.com/orbitflare/orbit-cli)
* Rust SDK: [github.com/orbitflare/orbitflare-sdk-rs](https://github.com/orbitflare/orbitflare-sdk-rs)
The agent surfaces are derived from the same source of truth: [docs.orbitflare.com/llms.txt](https://docs.orbitflare.com/llms.txt). Point any agent at that index when you want it to ground answers in the official docs.
# Rust SDK
Source: https://docs.orbitflare.com/agents/sdk
Build production agents and indexers in Rust with type-safe clients for RPC, WebSocket, Yellowstone gRPC, and Jetstream, with retry, failover, and reconnection built in.
When you're past the prototype stage and shipping a production daemon, indexer, trading bot, or long-running agent worker, reach for the Rust SDK. Instead of managing raw HTTP requests, gRPC channels, and WebSocket connections yourself, the SDK handles connection lifecycle, authentication, retries, failover, and reconnection behind a clean API.
For interactive agent workflows in Claude Code or Cursor, prefer the [MCP server](/agents/mcp). For shell scripts and CI, prefer the [CLI](/agents/cli). The SDK is for code your agents *write*: production services that run independently.
## Install
```bash theme={null}
cargo add orbitflare-sdk # RPC only
cargo add orbitflare-sdk --features ws # + WebSocket
cargo add orbitflare-sdk --features grpc # + Yellowstone gRPC
cargo add orbitflare-sdk --features jetstream # + Jetstream
```
Combine features as needed, e.g. `--features "ws grpc"` for an indexer that subscribes to both surfaces.
## What's included
| Service | What it does |
| - | - |
| **RPC** | JSON-RPC client with typed helpers for common Solana methods, plus raw escape hatches |
| **WebSocket** | Subscriptions for accounts, logs, slots, signatures with auto-resubscribe on reconnect |
| **gRPC (Yellowstone)** | Yellowstone Geyser streaming for transactions, accounts, slots, and blocks. YAML config or programmatic filters |
| **JetStream** | OrbitFlare's decoded shreds delivered as gRPC streams. Same client pattern as Yellowstone, different proto |
## Environment variables
The SDK reads endpoints from the environment if you don't pass them explicitly. This is the recommended pattern for agent-generated code: the agent never has to hard-code URLs or keys, and the same binary works across regions and networks.
| Variable | Used by | Purpose |
| - | - | - |
| `ORBITFLARE_LICENSE_KEY` | RPC, WebSocket | API key appended to endpoint URLs |
| `ORBITFLARE_RPC_URL` | RPC | Default endpoint if `.url()` is not called |
| `ORBITFLARE_WS_URL` | WebSocket | Default endpoint if `.url()` is not called |
| `ORBITFLARE_GRPC_URL` | gRPC | Default endpoint if `.url()` is not called |
| `ORBITFLARE_JETSTREAM_URL` | JetStream | Default endpoint if `.url()` is not called |
## Quick example
A minimal RPC agent that reads its endpoint from the environment and queries balance with retry/failover handled automatically:
```rust theme={null}
use orbitflare_sdk::{RpcClientBuilder, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = RpcClientBuilder::new().build()?; // reads ORBITFLARE_RPC_URL + LICENSE_KEY
let lamports = client.get_balance("Gh9ZwEm...").await?;
println!("{} SOL", lamports as f64 / 1e9);
Ok(())
}
```
With explicit failover regions:
```rust theme={null}
let client = RpcClientBuilder::new()
.urls(&[
"http://ny.rpc.orbitflare.com",
"http://fra.rpc.orbitflare.com",
"http://ams.rpc.orbitflare.com",
])
.build()?;
```
The first URL is primary; the rest are fallbacks tried in order on failure.
## Streaming with Yellowstone gRPC
```rust theme={null}
use orbitflare_sdk::{GeyserClientBuilder, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = GeyserClientBuilder::new()
.url("http://fra.rpc.orbitflare.com:10000")
.build()
.await?;
let mut stream = client.subscribe_yaml("stream.yml")?;
while let Some(update) = stream.next().await {
// process update
}
Ok(())
}
```
`subscribe_yaml` reads filters from a YAML config; for programmatic filters use `client.subscribe(SubscribeRequest { .. })` instead. The Jetstream client mirrors this pattern. Swap `GeyserClientBuilder` for `JetstreamClientBuilder`.
## Why use the SDK over raw HTTP
For agent-written production code, the SDK handles the cross-cutting concerns that are easy to get wrong:
* **Retries with exponential backoff**, configurable per client
* **Multi-region failover**: primary fails, fallbacks tried in order
* **Auth injection**: license key added to every request, never stored in the endpoint
* **WebSocket reconnect**: subscriptions auto-resubscribe after disconnect
* **gRPC channel management**: keepalive, reconnect, backoff
* **Typed responses**: common methods deserialize into Rust structs, with raw escape hatches available for everything else
When you ask an agent to write an OrbitFlare integration, point it at the SDK and the resulting code stays small, idiomatic, and resilient.
## Source
* SDK repo: [github.com/orbitflare/orbitflare-sdk-rs](https://github.com/orbitflare/orbitflare-sdk-rs)
* Full reference: [SDK overview](/sdk/overview), [RPC](/sdk/rust-rpc), [WebSocket](/sdk/rust-websocket), [gRPC](/sdk/rust-grpc), [Jetstream](/sdk/rust-jetstream)
# Skills
Source: https://docs.orbitflare.com/agents/skills
Drop-in skill packages that teach Claude Code, Cursor, and Codex how to build on OrbitFlare correctly.
OrbitFlare ships agent skills (instruction sets the model loads at the start of a session) so coding agents write OrbitFlare code correctly without you fetching docs first. The main skill covers HTTP RPC, WebSockets, Jetstream, Yellowstone gRPC, raw Shredstream, Dedicated Nodes (Solana + BNB Chain), `getTransactionsForAddress`, historical archive data, Metis Swap, Jito Bundle Simulation, the Customer API, the OrbitFlare CLI, and the `orbitflare-sdk-rs` Rust SDK.
Skills are read-only context; they don't make API calls. Pair a skill with the [MCP server](/agents/mcp) when you want the agent to actually query OrbitFlare from chat.
## Install
One line, no clone required. Pick the host:
```bash theme={null}
npx @orbitflare/skills@latest
```
Installs to `~/.claude/skills/orbitflare`. Available in every Claude Code session.
```bash theme={null}
npx @orbitflare/skills@latest --project
```
Installs to `./.claude/skills/orbitflare`. Only loaded when Claude Code runs in this directory, so commit it and your team picks it up automatically.
```bash theme={null}
npx @orbitflare/skills@latest --cursor
```
Installs to `~/.cursor/skills-cursor/orbitflare`.
```bash theme={null}
npx @orbitflare/skills@latest --codex
```
Installs to `~/.codex/skills/orbitflare`.
```bash theme={null}
npx @orbitflare/skills@latest --path /your/custom/dir
```
Installs to `/your/custom/dir/orbitflare`.
After install, **restart your agent** so it picks up the new skill.
Updates are one command: `npx @orbitflare/skills@latest` again. The installer overwrites the skill files in place.
### From source
If you want to clone the repo (e.g. to modify the skill), the underlying installer is also exposed as a shell script:
```bash theme={null}
git clone https://github.com/orbitflare/orbitflare-skills
cd orbitflare-skills/orbitflare
./install.sh # same flags: --project, --cursor, --codex, --path
```
## What's in the skill
`SKILL.md` is the entry point. The model loads it at the start of every session and uses it to route to the right reference file. References cover:
| Reference | Topic |
| - | - |
| `rpc.md` | HTTP JSON-RPC: endpoint URLs, headers, common methods |
| `websockets.md` | WebSocket subscriptions, reconnection patterns |
| `jetstream.md` | OrbitFlare's Shredstream-decoded gRPC stream |
| `yellowstone.md` | Yellowstone Geyser gRPC: accounts, txs, slots, blocks |
| `shredstream.md` | Raw UDP Shredstream feed |
| `fetching-data.md` | `getTransactionsForAddress`, historical archive |
| `dedicated-nodes.md` | Solana + BNB Chain dedicated infrastructure |
| `trading-apis.md` | Metis Swap, Jito Bundle Simulation |
| `customer-api.md` | Customer API v2: licenses, billing, top-up |
| `cli-sdk.md` | OrbitFlare CLI command surface + Rust SDK setup |
| `onboarding.md` | Auth keys, dashboard signup, first call |
| `repos.md` | Pointers to template repos and example projects |
The skill instructs the agent to read the relevant reference **before** writing code, so you get correct endpoint URLs, gRPC filter shapes, and SDK feature flags on the first try.
## Try it
After installing the skill and restarting your agent:
> "Stream pump.fun trades using OrbitFlare Jetstream from Frankfurt"
> "Quote 1 SOL → USDC with Metis on OrbitFlare and write a Rust binary that polls every 30 seconds"
> "Build a Yellowstone gRPC subscription that streams Jupiter swap transactions, with retry on disconnect"
The agent will pull the relevant reference (`jetstream.md`, `trading-apis.md`, `yellowstone.md`) and produce code that uses the right endpoint URL, headers, and SDK patterns.
## Source
* Skills repo: [github.com/orbitflare/orbitflare-skills](https://github.com/orbitflare/orbitflare-skills)
* npm: [`@orbitflare/skills`](https://www.npmjs.com/package/@orbitflare/skills)
* Source of truth: [docs.orbitflare.com/llms.txt](https://docs.orbitflare.com/llms.txt)
Everything in the skill is derived from this documentation site. When in doubt, point the agent at the `llms.txt` index.
# Authentication & Limits
Source: https://docs.orbitflare.com/authentication
Authenticate Solana JSON-RPC with your license key on the query string, understand per-plan rate and connection limits, HTTP semantics, and errors for production workloads on OrbitFlare.
## RPC Authentication
All OrbitFlare RPC requests are authenticated by appending your license key as a query parameter to the endpoint URL.
```bash theme={null}
curl -X POST "https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockHeight"
}'
```
Your RPC license key is provided with your OrbitFlare service subscription. You can find it in the **Licenses** section of your [OrbitFlare Dashboard](https://orbitflare.com/dashboard).
## Customer API Authentication
The [Customer API](/api-documentation/welcome) uses a different authentication method. Pass your API key via the `X-ORBIT-KEY` HTTP header:
```bash theme={null}
curl -X GET https://orbitflare.com/api/customer/v2/licenses \
-H "X-ORBIT-KEY: YOUR_API_KEY"
```
Customer API v2 also supports Bearer token authentication obtained through the device flow or wallet signature auth. See the [Customer API docs](/api-documentation/welcome) for details.
## RPC Endpoints
OrbitFlare provides geo-distributed RPC endpoints so you can route requests to the region closest to your users or infrastructure. The URL format is:
```
https://{region}.rpc.orbitflare.com?api_key=YOUR_API_KEY
```
Replace `{region}` with one of the region codes below.
### Available Regions
| City | Region Code | Endpoint |
| - | - | - |
| Ashburn | `va` | `https://va.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| New York | `ny` | `https://ny.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| Los Angeles | `la` | `https://la.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| Salt Lake City | `slc` | `https://slc.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| City | Region Code | Endpoint |
| - | - | - |
| Amsterdam | `ams` | `https://ams.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| Frankfurt | `fra` | `https://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| London | `lon` | `https://lon.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| City | Region Code | Endpoint |
| - | - | - |
| Tokyo | `jp` | `https://jp.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| Singapore | `sg` | `https://sg.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
### Choosing a Region
Endpoints are region-pinned. Pick the region closest to your infrastructure for the lowest latency, for example Frankfurt:
```
https://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY
```
Latency to the nearest region is what matters most. If you are unsure which region is closest to your servers, test a few with the `/health` endpoint and compare round-trip times.
## WebSocket Endpoints
WebSocket connections use the same region codes with the `wss://` scheme:
```
wss://{region}.rpc.orbitflare.com?api_key=YOUR_API_KEY
```
For example, Frankfurt:
```
wss://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY
```
Authentication is handled through the same `api_key` query parameter.
## Devnet
OrbitFlare provides a dedicated Devnet endpoint for development and testing:
```
https://devnet.rpc.orbitflare.com?api_key=YOUR_API_KEY
```
The Devnet endpoint connects to the Solana Devnet. Do not use it for production workloads. Devnet tokens have no monetary value.
## Rate Limits by Plan
Each plan defines a maximum number of **requests per second (RPS)** and **transactions per second (TPS)**.
| Plan | Requests per Second (RPS) | Transactions per Second (TPS) |
| - | :-: | :-: |
| Free | 10 | 1 |
| Developer | 50 | 10 |
| Growth | 200 | 75 |
| Scale | 400 | 150 |
| Pro | 600 | 200 |
| Dedicated | Unlimited | Unlimited |
Requests that exceed your plan's RPS or TPS limit will receive an HTTP `429 Too Many Requests` response. Implement exponential backoff in your client to handle these gracefully.
No. OrbitFlare does not impose monthly credit limits or request caps. You can send unlimited total requests within your plan's per-second rate limit.
OrbitFlare plans have **no credit limits** -- you can make unlimited requests as long as you stay within your plan's per-second rate tier. There are no surprise overage charges.
## Data Streaming Connection Limits
Connection limits apply independently of your RPC rate limit tier and are enforced per IP address across all streaming interfaces.
### gRPC (Jetstream / Yellowstone)
| Limit | Value |
| - | - |
| Concurrent connections per IP | **50** |
| Subscriptions per connection | Unlimited |
| Idle connection timeout | 10 minutes (use ping to keep alive) |
The 50-connection cap is shared across all gRPC endpoints (Jetstream and Yellowstone) originating from the same IP address. This applies regardless of which region you connect to.
Dedicated gRPC nodes are not subject to the shared connection limit. If you need more than 50 concurrent gRPC connections, contact the team about a dedicated gRPC node.
**Keeping connections alive**
Cloud load balancers and proxies (including Cloudflare) may close idle gRPC streams after \~10 minutes. Send a ping every 30 seconds to prevent disconnection:
```typescript theme={null}
const pingRequest: SubscribeRequest = {
ping: { id: 1 },
accounts: {},
accountsDataSlice: [],
transactions: {},
transactionsStatus: {},
blocks: {},
blocksMeta: {},
slots: {},
entry: {},
};
setInterval(() => {
stream.write(pingRequest, (err) => {
if (err) console.error("Ping failed:", err);
});
}, 30_000);
```
**What happens when the limit is exceeded**
When your IP has 50 active gRPC connections and you attempt to open a new one, the server returns a gRPC `RESOURCE_EXHAUSTED` status:
```
StatusCode: RESOURCE_EXHAUSTED
Message: connection limit exceeded
```
Close unused streams before opening new ones. Implement exponential backoff when reconnecting after a `RESOURCE_EXHAUSTED` error.
### WebSocket
| Limit | Value |
| - | - |
| Concurrent connections per IP | **50** |
| Subscriptions per connection | Varies by plan |
| Idle connection timeout | 60 seconds without a message |
WebSocket connections use the same 50 concurrent connections per IP limit. Connections idle for more than 60 seconds without any message exchange will be closed by the server.
**What happens when the limit is exceeded**
New WebSocket connections attempted beyond the limit are rejected with a `1008 Policy Violation` close code:
```
WebSocket closed: 1008 Policy Violation - connection limit exceeded
```
The 50-connection limit applies to all shared plans. If your workload requires more concurrent streaming connections, a **Dedicated gRPC Node** or **Dedicated RPC Node** removes the shared limit entirely. Contact the team on [Discord](https://discord.gg/orbitflare) to discuss options.
The limit is enforced globally per IP address, across all regions. A single IP with 30 connections to Frankfurt and 20 connections to New York has reached the 50-connection cap.
Use exponential backoff when reconnecting after a connection error. Start with a 1-second delay, doubling on each failed attempt up to a maximum of 30 seconds. Always close streams cleanly before reconnecting to avoid exhausting your connection quota.
# BNB Chain Nodes
Source: https://docs.orbitflare.com/bnb-chain
Dedicated BNB Chain and BSC nodes for DeFi, trading desks, and high-throughput apps—predictable latency, archival options, and enterprise operations from OrbitFlare.
## Overview
OrbitFlare provides enterprise-grade dedicated node infrastructure for BNB Chain (formerly Binance Smart Chain). Purpose-built for DeFi protocols, trading operations, and high-throughput applications that require reliable, low-latency access to the BNB/BSC network.
## Key Features
Throughput scales with your plan, from 25 RPS on Free to 1,000 RPS on Performance. Dedicated instances are unmetered.
Your node runs on isolated, high-performance hardware — no noisy neighbors, no shared resources.
Enterprise-grade uptime guarantee backed by a service level agreement with financial commitments.
Deploy nodes across 11 regions worldwide to minimize latency for your users and trading infrastructure.
Around-the-clock support from the infrastructure team for monitoring, incidents, and configuration changes.
Complete API coverage including WebSocket subscriptions and full archive node data access.
## Pricing
Throughput scales by plan. RPC, WebSockets, and archival data are included on every tier, and there are no monthly credit or request caps.
| Plan | Price | Requests / Second |
| - | - | :-: |
| Free | \$0/mo | 25 |
| Starter | \$49/mo | 100 |
| Growth | \$149/mo | 300 |
| Pro | \$299/mo | 600 |
| Performance | \$499/mo | 1,000 |
Isolated infrastructure for high-throughput workloads:
| Feature | Details |
| - | - |
| **Throughput** | Custom, unmetered |
| **Hardware** | 100% dedicated resources, isolated environment |
| **Rate Limits** | Custom, configurable |
| **Support** | Priority engineering support, 24/7 |
| **Uptime SLA** | 99.99%, custom terms available |
| **Pricing** | Quoted on throughput, region, and SLA |
For teams that need more than a single node or require tailored configurations:
| Feature | Details |
| - | - |
| **Nodes** | Multiple dedicated nodes |
| **Configuration** | Custom node configs and parameters |
| **Account Management** | Dedicated account manager |
| **SLA** | Custom uptime and response SLA |
| **Pricing** | Volume discounts available |
Reach out to the sales team to discuss your requirements and get a custom quote.
## Use Cases
Power AMMs, lending platforms, and yield aggregators with dedicated, low-latency node access.
Run arbitrage, MEV, and high-frequency trading strategies on infrastructure that won't throttle you.
Query multiple liquidity sources in parallel without hitting shared rate limits.
Handle minting events, metadata indexing, and high-traffic marketplace operations reliably.
Operate cross-chain bridges with the uptime guarantees and monitoring that critical infrastructure demands.
## Connecting
BNB Chain is served from a single global endpoint that follows the same API structure as the official BSC RPC.
### Authentication
Append your license key as a query parameter:
```
https://bsc.rpc.orbitflare.com?api_key=YOUR_API_KEY
```
### Making RPC Calls
BNB Chain uses the same JSON-RPC 2.0 format as Ethereum. Pass `Content-Type: application/json` with a POST body:
```bash cURL theme={null}
curl -X POST "https://bsc.rpc.orbitflare.com?api_key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://bsc.rpc.orbitflare.com?api_key=YOUR_API_KEY",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_blockNumber",
params: [],
id: 1,
}),
}
);
const data = await response.json();
console.log("Latest block:", parseInt(data.result, 16));
```
```python Python theme={null}
import requests
response = requests.post(
"https://bsc.rpc.orbitflare.com?api_key=YOUR_API_KEY",
headers={"Content-Type": "application/json"},
json={
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1,
},
)
data = response.json()
print("Latest block:", int(data["result"], 16))
```
### WebSocket Subscriptions
Real-time subscriptions use the `wss://` scheme with the same endpoint:
```javascript theme={null}
const ws = new WebSocket(
"wss://bsc.rpc.orbitflare.com?api_key=YOUR_API_KEY"
);
ws.on("open", () => {
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_subscribe",
params: ["newHeads"],
}));
});
ws.on("message", (data) => {
const msg = JSON.parse(data);
console.log("New block header:", msg.params?.result);
});
```
### Supported Methods
Your dedicated node exposes the complete BNB Chain / BSC JSON-RPC API including:
| Category | Methods |
| - | - |
| **Blocks** | `eth_blockNumber`, `eth_getBlockByHash`, `eth_getBlockByNumber` |
| **Transactions** | `eth_sendRawTransaction`, `eth_getTransactionByHash`, `eth_getTransactionReceipt` |
| **Accounts** | `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getTransactionCount` |
| **Contracts** | `eth_call`, `eth_estimateGas`, `eth_getLogs` |
| **Network** | `net_version`, `eth_chainId`, `eth_gasPrice`, `eth_feeHistory` |
| **WebSocket** | `eth_subscribe` (newHeads, logs, newPendingTransactions) |
BNB Chain uses `chainId: 56` for mainnet and `chainId: 97` for testnet (BSC Testnet). Your dedicated node connects to mainnet by default.
## Rate Limits and Quotas
These limits apply to all OrbitFlare EVM chains (Robinhood, Polygon, BSC). They are enforced per license, and HTTP and WebSocket traffic on the same key share a single rate limit and in-flight budget.
| Control | Free | Paid |
| - | - | - |
| Requests / second | plan RPS | plan RPS |
| Batch size (HTTP + WS) | 1 item | 100 items |
| `eth_getLogs` in flight | 1 | 10 |
| `eth_getLogs` block range | 150 blocks | 10,000 blocks |
| `debug_*` / `trace_*` methods | blocked | allowed |
| WebSocket connections | 1 | from 10 |
| Subscriptions per WebSocket connection | 5 | 25 |
| `logs` subscription without a filter | refused | allowed |
| WebSocket data per connection | 1 GiB | unlimited |
Applies to both tiers: WebSocket connection lifetime of 24 hours; idle timeout of 30 minutes with no data frames; frame size of 4 MiB inbound and 32 MiB outbound; each batch item counts as one request.
### Error Codes
| HTTP | JSON-RPC | Meaning |
| - | - | - |
| 429 | `-32429` | Rate limit exceeded |
| 429 | `-32005` | Concurrent, batch, connection, or subscription limit exceeded |
| 400 | `-32602` | `eth_getLogs` block range too large (the message names the span) |
| 403 | `-32601` | Method not available on this plan |
| 403 | `-32406` | WebSockets not enabled for this key |
`-32005` and `-32429` are transient. Retry with exponential backoff. If `-32005` persists on isolated calls, another client is likely using the same key concurrently, since HTTP and WebSocket share one in-flight budget.
## Get Started
Talk to the OrbitFlare team about dedicated BNB Chain nodes, custom configurations, and enterprise pricing.
Learn how API keys work, endpoint formats, and connection limits.
# OrbitFlare CLI
Source: https://docs.orbitflare.com/cli
Full-featured command-line interface for OrbitFlare. Streaming, querying, scaffolding, account management, and a built-in TUI dashboard, all from your terminal.
The OrbitFlare CLI is the single entry point for everything a developer needs: streaming on-chain data via gRPC, querying the blockchain, scaffolding projects from templates, and managing your account through an interactive dashboard.
The CLI stores credentials in your OS keychain and supports multiple auth profiles, so you can switch between accounts or networks instantly.
## Installation
```bash theme={null}
cargo install orbitflare
```
```bash theme={null}
git clone https://github.com/orbitflare/orbit-cli.git
cd orbit-cli
cargo install --path .
```
Requires **Rust 1.85+**. Verify the installation:
```bash theme={null}
orbitflare --version
```
## Quick Start: Existing Users
If you already have an OrbitFlare account and API key from the [dashboard](https://orbitflare.com/dashboard):
```bash theme={null}
# Authenticate with your API key
orbitflare auth login --x-orbit-key YOUR_API_KEY
# Point the CLI to your RPC endpoint (from your dashboard)
orbitflare config set rpc.url ORBITFLARE_RPC_URL
# Verify everything works
orbitflare ping
orbitflare rpc slot
```
A fresh install starts with an empty config. After login, set at least `rpc.url` so commands like `ping` and `rpc` know where to connect. Your endpoint URLs are available in the **Services** section of your [dashboard](https://orbitflare.com/dashboard).
Endpoint URLs follow this format:
| Protocol | Format | Example |
| - | - | - |
| RPC | `http://{region}.rpc.orbitflare.com` | `http://ams.rpc.orbitflare.com` |
| WS | `ws://{region}.rpc.orbitflare.com` | `ws://ams.rpc.orbitflare.com` |
| gRPC (Yellowstone) | `http://{region}.rpc.orbitflare.com:10000` | `http://ams.rpc.orbitflare.com:10000` |
| Jetstream | `http://{region}.jetstream.orbitflare.com` | `http://ams.jetstream.orbitflare.com` |
## Quick Start: New Users
Sign up at [orbitflare.com](https://orbitflare.com) and choose a plan.
The CLI supports three login methods. Pick the one that suits your workflow:
```bash theme={null}
# API key (from orbitflare.com/dashboard)
orbitflare auth login --x-orbit-key YOUR_API_KEY
# Solana wallet keypair
orbitflare auth login --wallet ~/.config/solana/id.json
# Device flow (opens browser)
orbitflare auth login
```
The CLI fetches and stores your RPC license key automatically during login. To set it manually:
```bash theme={null}
orbitflare auth set-license-key YOUR_LICENSE_KEY
```
Keep your API key and license key secret. Do not commit them to version control or expose them in client-side code.
A fresh install starts with an empty config. Point the CLI to your OrbitFlare endpoints:
Your endpoint URLs are in the **Services** section of your [dashboard](https://orbitflare.com/dashboard). They follow the format `http://{region}.rpc.orbitflare.com`.
```bash theme={null}
# Set your RPC endpoint
orbitflare config set rpc.url ORBITFLARE_RPC_URL
# (Optional) Set streaming endpoints
orbitflare config set grpc.url ORBITFLARE_GRPC_URL
orbitflare config set jetstream.url ORBITFLARE_JETSTREAM_URL
# Verify connectivity
orbitflare ping
```
See [Authentication & Limits](/authentication) for all available region endpoints.
```bash theme={null}
# Query your SOL balance
orbitflare rpc balance YOUR_WALLET_ADDRESS
# Get current slot
orbitflare rpc slot
# Scaffold a project
orbitflare template --list
orbitflare template --install solana-copy-trader
# Launch the TUI dashboard
orbitflare dashboard
```
## Authentication
Credentials are stored securely in your OS keychain. The CLI supports multiple named profiles so you can manage separate accounts or environments.
```bash theme={null}
# Check current auth status
orbitflare auth status
# Switch profile
orbitflare auth switch --profile work
# Log out
orbitflare auth logout
```
### Auth Methods
Authenticate with your API key from the [dashboard](https://orbitflare.com/dashboard). The key is passed via the `X-ORBIT-KEY` header to the Customer API.
```bash theme={null}
orbitflare auth login --x-orbit-key YOUR_API_KEY
```
Authenticate by signing a challenge with your Solana keypair. If the wallet is not linked to an account, the CLI will walk you through registration.
```bash theme={null}
orbitflare auth login --wallet ~/.config/solana/id.json
```
Opens your browser for OAuth-style authentication. Ideal for interactive use.
```bash theme={null}
orbitflare auth login
```
The CLI displays a verification URL and code, polls for completion, then stores the session token automatically.
### Key Resolution Order
1. `--x-orbit-key` flag on the command (highest priority)
2. `--profile` flag to select a stored profile
3. Default profile in `~/.orbitflare/config.yml`
## Command Reference
The CLI has top-level commands spanning authentication, configuration, blockchain queries, real-time streaming, project scaffolding, and account management.
### Core Commands
| Command | Description |
| - | - |
| `auth login` | Authenticate (API key, wallet, or device flow) |
| `auth logout` | Remove stored credentials |
| `auth status` | Show current authentication status |
| `auth switch --profile ` | Switch to a different auth profile |
| `auth set-license-key ` | Store RPC license key for current profile |
| `config show` | Display all configuration values |
| `config set ` | Set a configuration value |
| `config remove ` | Remove a value from a list config |
| `config reset` | Reset config to defaults (preserves auth) |
| `ping` | Check connectivity to all configured services |
| `ping --service ` | Ping a specific service (`rpc`, `jetstream`, `grpc`) |
| `docs [TOPIC...]` | Open documentation in your browser |
| `plan list` | List available RPC plans |
| `plan view ` | View plan details |
| `plan compare --all` | Compare all plans side by side |
| `pay check-balance` | Check account balance |
| `pay topup ` | Top up balance with USDC (on-chain) |
| `pay history` | Top-up / credit history |
| `pay purchase ` | Purchase a plan from balance |
| `pay order --history` | List orders |
| `pay order ` | View order details |
| `pay invoice --list` | List invoices |
| `pay invoice ` | View invoice details |
| `pay renew ` | Pay invoice from balance (renewal) |
### RPC Commands
Query the Solana blockchain directly from your terminal.
```bash theme={null}
orbitflare rpc account
orbitflare rpc tokens
orbitflare rpc balance
orbitflare rpc tx
orbitflare rpc history --limit 20
orbitflare rpc program-accounts --limit 10
orbitflare rpc slot
orbitflare rpc epoch
orbitflare rpc blockhash
orbitflare rpc stats
orbitflare rpc priority-fees --account
orbitflare rpc raw '{"jsonrpc":"2.0","id":1,"method":"getSlot","params":[]}'
```
### WebSocket Commands
Subscribe to live Solana pubsub events over WebSocket. Each subscription runs until you press `Ctrl+C` and prints events as they arrive (use `--json` for machine-readable output).
```bash theme={null}
# Live slot updates
orbitflare ws slot
# Account changes for a specific address
orbitflare ws account
# Transaction logs mentioning one or more addresses (omit --mentions for all)
orbitflare ws logs --mentions --mentions
# Confirmation of a single transaction signature
orbitflare ws signature
```
The endpoint is derived from your configured `rpc.url`: `http://` becomes `ws://` and `https://` becomes `wss://`. To target a different host for a single command, pass `--ws-url`:
```bash theme={null}
orbitflare ws slot --ws-url ws://ams.rpc.orbitflare.com
```
### Streaming Commands
Stream real-time Solana data over gRPC. Both commands take a YAML config file that defines your filters and reconnection strategy.
```bash theme={null}
orbitflare jet --config stream.yml
```
Jetstream provides ultra-low-latency transaction and account streaming.
```yaml stream.yml theme={null}
transactions:
pump_trades:
account_include:
- "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
accounts:
my_wallet:
account:
- "YOUR_WALLET_ADDRESS"
reconnect:
initial_delay_ms: 100
max_delay_ms: 30000
multiplier: 2.0
max_retries: 0 # 0 = unlimited
```
```bash theme={null}
orbitflare grpc --config grpc-stream.yml
```
Yellowstone provides the complete Geyser plugin data stream including slots and block metadata.
```yaml grpc-stream.yml theme={null}
transactions:
jupiter_swaps:
account_include:
- "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"
vote: false
failed: false
slots:
all_slots:
filter_by_commitment: true
commitment: confirmed
reconnect:
initial_delay_ms: 100
max_delay_ms: 30000
multiplier: 2.0
max_retries: 0
```
Both streaming commands support automatic endpoint failover and exponential backoff reconnection. Environment variables can be used in config files with `${VAR}` syntax.
### Template Commands
Scaffold projects from production-ready starter templates.
```bash theme={null}
# List all templates
orbitflare template --list
# Filter by language
orbitflare template --list --filter rust
# View template details
orbitflare template --view solana-copy-trader
# Install a template
orbitflare template --install solana-copy-trader --dir ./my-bot
```
Templates are fetched from the [orbitflare/templates](https://github.com/orbitflare/templates) repository and cached locally for offline use.
### Plan Commands
Browse and compare available RPC plans. Plan data is cached locally for 6 hours.
```bash theme={null}
# List all plans (sorted by price)
orbitflare plan list
# View plan details
orbitflare plan view
# Compare all plans side by side
orbitflare plan compare --all
# Compare specific plans
orbitflare plan compare dev growth
```
### Payment Commands
Manage your account balance, purchase plans, and handle invoices. Only `topup` requires a Solana wallet — everything else uses your account balance.
```bash theme={null}
# Check account balance
orbitflare pay check-balance
# Top up balance with USDC (on-chain)
orbitflare pay topup --wallet ~/.config/solana/id.json
# Top-up / credit history
orbitflare pay history
# Purchase a plan from balance
orbitflare pay purchase --coupon
# View orders
orbitflare pay order --history
orbitflare pay order
# View invoices
orbitflare pay invoice --list
orbitflare pay invoice
# Pay an invoice from balance (renewal)
orbitflare pay renew
```
The `topup` command defaults to `~/.config/solana/id.json` for the wallet keypair. Use `--wallet` to specify a different path. Billing periods: `monthly`, `quarterly`, `semi-annual`, `annual`.
The `purchase` command runs a dry-run validation first, showing you the pricing breakdown and whether your balance is sufficient before confirming.
### Dashboard
Launch an interactive TUI for managing your OrbitFlare account without leaving the terminal.
```bash theme={null}
orbitflare dashboard
```
The dashboard provides full keyboard-driven access to your profile, services, API keys, and invoices.
| Key | Action |
| - | - |
| `q` | Quit |
| `Esc` | Back |
| `Tab` | Switch tab / focus |
| `↑↓` / `jk` | Navigate |
| `Enter` | Select / expand / toggle |
| `t` / `T` | Cycle theme |
| `[` | Toggle sidebar |
| `R` | Refresh data |
| Key | Tab | Action |
| - | - | - |
| `m` | Details | Toggle auth mode (IP / API Key) |
| `w` | Details | Toggle auto-renewal |
| `r` | Details | Regenerate license key |
| `n` | IPs / Domains | Switch Mainnet / Devnet |
| `a` | IPs / Domains | Add IP or domain |
| `d` | IPs / Domains | Remove IP or domain |
| Key | Action |
| - | - |
| `c` | Create new API key |
| `e` | Toggle enabled / disabled |
| `r` | Regenerate key |
| `x` | Delete key |
| `a` | Add IP (when expanded) |
| `d` | Remove IP (when expanded) |
| Key | Section | Action |
| - | - | - |
| `p` | Invoices | Pay with account balance |
| `e` | Profile | Edit name and payment wallet |
## JSON Output
All commands support `--json` for machine-readable output, making the CLI easy to integrate into scripts and automation pipelines.
```bash theme={null}
orbitflare rpc slot --json
orbitflare rpc balance Gh9ZwEm... --json
orbitflare auth status --json
```
Example output:
```json theme={null}
{
"confirmed": 312849571,
"finalized": 312849539
}
```
## Configuration
Configuration is stored in `~/.orbitflare/config.yml`. Manage it with the `config` command:
```bash theme={null}
# View current config
orbitflare config show
# Set network
orbitflare config set network devnet
# Set RPC endpoint
orbitflare config set rpc.url http://ams.rpc.orbitflare.com
# Set gRPC and Jetstream endpoints
orbitflare config set grpc.url http://ams.rpc.orbitflare.com:10000
orbitflare config set jetstream.url http://ams.jetstream.orbitflare.com
# Set commitment level
orbitflare config set commitment finalized
# Change TUI theme
orbitflare config set theme dracula
# Set template repository
orbitflare config set templates.repo https://github.com/your-org/templates
# Reset to defaults
orbitflare config reset
```
Devnet endpoints are preconfigured by default:
* **RPC:** `http://devnet.rpc.orbitflare.com` (fallback: `https://api.devnet.solana.com`)
* **gRPC:** `http://devnet.rpc.orbitflare.com:10000`
Use `--network devnet` to switch at runtime.
### File Locations
```bash theme={null}
~/.orbitflare/
├── config.yml # Main configuration
├── templates_cache.json # Template registry cache
└── cache/
└── rpc-plans.json # Plans cache (6-hour TTL)
```
API keys, tokens, and license keys are stored in your **OS Keychain**, not on disk.
## Global Flags
These flags work with every command and override the corresponding config value for that invocation.
| Flag | Description |
| - | - |
| `--json` | Output raw JSON |
| `--raw` | Minimal output, no formatting |
| `--network ` | Override network (`mainnet` / `devnet` / `testnet`) |
| `--commitment ` | Override commitment (`processed` / `confirmed` / `finalized`) |
| `--rpc-url ` | Override RPC endpoint |
| `--grpc-url ` | Override gRPC endpoint |
| `--ws-url ` | Override WebSocket endpoint (default: derived from RPC URL) |
| `--fallback-url ` | Fallback URLs (repeatable) |
| `--profile ` | Use a specific auth profile |
| `--quiet` | Suppress non-essential output |
| `--no-color` | Disable colored output |
## Full Workflow Example
A complete walkthrough from installation to querying:
```bash theme={null}
# 1. Install the CLI
cargo install orbitflare
# 2. Authenticate
orbitflare auth login --x-orbit-key YOUR_API_KEY
# 3. Configure endpoints (from your dashboard)
orbitflare config set rpc.url ORBITFLARE_RPC_URL
orbitflare config set grpc.url ORBITFLARE_GRPC_URL
# 4. Verify connectivity
orbitflare ping
# 5. Query the blockchain
orbitflare rpc slot
orbitflare rpc balance YOUR_WALLET_ADDRESS
# 6. Scaffold a project
orbitflare template --install solana-copy-trader --dir ./my-bot
# 7. Stream real-time data
orbitflare jet --config stream.yml
```
## What's Next?
Explore the full Solana RPC method reference.
Learn about real-time gRPC data streaming.
Complete Geyser plugin data via Yellowstone.
Browse production-ready project templates.
## Support
Join the community for help and discussions.
Report issues or contribute to the CLI.
# OrbitFlare Jetstream
Source: https://docs.orbitflare.com/data-streaming/jetstream
High-performance gRPC streaming for Solana — enrichment, slot events, and dynamic filters
Jetstream is OrbitFlare's low-latency stream of Solana transactions, with **opt-in per-transaction
enrichment**, **slot lifecycle events**, and **dynamic filters** you can change without
reconnecting — so you can build realtime apps (alerts, trading, analytics, monitoring) without
running your own indexing pipeline.
## Overview
OrbitFlare Jetstream is a high-performance gRPC service for real-time streaming of Solana
transactions. It delivers a minimal-latency feed over a rich, flexible protocol — letting you adjust
your subscription on the fly and receive pre-computed transaction metadata so your client does less
work.
## Highlights
* **Dynamic filters** — add and remove filters while the stream is open, each with a client-chosen
`filter_id` that is echoed on every matching transaction. No reconnect required, and each change is
acknowledged with a `FilterResult`.
* **Opt-in enrichment** — request `fee_payer`, `writable_accounts`, `program_ids`, `compute_unit_price`
(the compute-unit price, micro-lamports/CU — not the total fee), `compute_limit`, `tx_size`, and
resolved address-lookup-table addresses directly on each transaction, instead of re-deriving them
client-side.
* **Slot lifecycle streaming** — a dedicated `SubscribeSlots` stream emits `ALIVE` / `COMPLETE` /
`DEAD` events per slot, with the current leader and parent slot.
* **Built-in liveness** — every message carries a monotonic `sequence` number for gap detection, and
the server emits a `Heartbeat` on an idle transaction stream.
* **Enrichment on demand** — a transaction is one flat message with the core fields by default;
switch on enrichment per subscription to add the extra fields whenever you want them.
## Authentication
Supply your API key in the `x-token` gRPC metadata header, or use IP-whitelist authentication. See
[Authentication](/authentication) for setup details.
## Connecting
1. Pick the [region endpoint](/data-streaming/jetstream-reference#available-endpoints) closest to your
infrastructure.
2. Generate a client from the `jetstream.v2` protobuf definition — see
[Code Generation](/data-streaming/jetstream-reference#code-generation) in the reference.
3. Authenticate with your `x-token` API key (or from a whitelisted IP) and open a stream.
## How streaming works
`SubscribeTransactions` is a **bidirectional** stream — you manage your subscription with the same
connection you receive data on:
1. Open the stream and send an `AddFilters` request containing one or more filters. Each filter needs
at least one of `account_include` / `account_exclude` / `account_required`. (Empty
match-everything filters are rejected.) `account_required` is **any-of** — a transaction matches if
it references at least one of the listed accounts, not all of them.
2. The server replies with a `FilterResult` per filter (`accepted`, or a `rejection_reason`).
3. Matching transactions arrive as `FilteredTransaction` messages, each listing the `filter_id`
values it matched.
4. Send `AddFilters` / `RemoveFilters` at any time to change what you receive — no reconnect.
5. On an idle transaction stream the server sends `Heartbeat` messages; use them (and `Ping`/`Pong`) to confirm liveness.
6. Track the `sequence` number to detect any gap, and reconnect (re-sending your filters) if the
stream closes.
To receive enriched transactions, set `include_enrichment: true` on a filter.
Enrichment is **subscription-wide**: if any one of your active filters sets `include_enrichment`,
every transaction the session receives is enriched. Turn it on per subscription whenever you want
the extra fields — it adds **no latency** to the stream; the only difference is a modestly larger
message.
## Slot events
Open `SubscribeSlots` (a separate, server-streaming RPC, no parameters) to receive a `SlotEvent` for
each slot transition:
* `ALIVE` — the first shred of the slot was received.
* `COMPLETE` — the slot's last shred was received.
* `DEAD` — the slot was skipped, or was superseded by later slots without completing.
Each event includes the `current_leader` (32-byte pubkey, when the current leader is available) and
the `parent_slot`.
## Migrating from v1
The earlier Jetstream v1 protocol is **deprecated** but still available for existing integrations. If
you are on v1, both services run side by side on the same endpoints, so migration is a client-side
protocol change with no network changes. See [Migrating from v1](/data-streaming/jetstream-v2-vs-v1)
for a field-by-field mapping, or the [deprecated v1 overview](/data-streaming/jetstream-v1).
## Best Practices
1. **Filtering** — always set at least one account constraint, and use a stable unique `filter_id` per
filter so you can correlate matches and remove filters later.
2. **Enrichment** — keep it off unless you need the extra fields; it applies to the whole subscription.
3. **Reconnection** — track `sequence` for gaps and reconnect automatically, re-sending your filters.
4. **Liveness** — rely on `Heartbeat` / `Ping` to detect a stalled connection.
## See Also
The field-by-field v1-to-Jetstream mapping and what each capability saves you.
Full Protocol Buffer specification, endpoints, and code generation.
The deprecated v1 overview, client examples, and filtering guide.
Full-fidelity Geyser-based streaming with inner instructions and complete metadata.
## Support
For technical support or questions about Jetstream, please contact our support team or join our
[Discord community](https://discord.gg/orbitflare).
# Dedicated Nodes
Source: https://docs.orbitflare.com/dedicated-nodes
Isolated Solana dedicated hardware with unlimited RPS and TPS, private RPC and streaming endpoints, priority support, and enterprise SLA—no shared-cluster noisy neighbors on OrbitFlare.
No rate limits on your dedicated infrastructure
Enterprise SLA-backed availability guarantee
Global deployment across major data centers
Dedicated engineering support around the clock
## Overview
OrbitFlare Dedicated Nodes provide 100% isolated Solana infrastructure reserved exclusively for your workloads. Unlike shared RPC services, dedicated nodes guarantee that your performance is never impacted by other users.
Every node runs on bare-metal servers allocated solely to your organization. There are no noisy neighbors, no shared CPU cycles, and no contention for memory or network bandwidth.
Tailor your node to your exact requirements -- choose your Solana client, configure RPC methods, adjust cache sizes, enable specific APIs, and fine-tune performance parameters to match your application's needs.
Your node operates in a completely isolated environment with dedicated network interfaces, storage volumes, and compute resources. This ensures consistent, predictable performance at all times.
## Pricing
**From \$2,500/mo**
* Unlimited RPS and TPS
* Dedicated bare-metal hardware
* Choice of Solana client
* Custom configuration
* Priority engineering support
* 99.99% uptime SLA
**From \$1,800/mo**
* Dedicated streaming resources
* Unlimited real-time data
* Optimized for high-frequency trading
* Low-latency gRPC streaming
* Priority engineering support
* 99.99% uptime SLA
**Contact Sales**
* Multiple dedicated nodes
* Custom SLA agreements
* On-premise deployment options
* Dedicated account manager
* Custom networking and peering
* Volume discounts available
## Available Solana Clients
Choose the Solana validator client that best fits your use case.
The standard Solana validator client maintained by Solana Labs. Battle-tested and widely deployed across the network, this is the default choice for most workloads.
* Broadest RPC method support
* Most extensively documented
* Default choice for general-purpose applications
A modified validator client with integrated MEV capabilities. Jito Labs client includes bundle support, Shredstream, and tip distribution -- ideal for trading applications and MEV strategies.
* Native bundle submission and simulation
* Shredstream for faster block propagation
* MEV tip distribution
* Optimized for trading and DeFi workloads
A high-performance validator client developed by Jump Crypto. Frankendancer combines components from the Firedancer project with the standard Solana runtime for significantly improved throughput and lower latency.
* Higher transaction throughput
* Lower end-to-end latency
* Optimized networking stack
* Ideal for performance-critical applications
## Dedicated vs Shared Comparison
| Feature | Dedicated Node | Shared RPC |
| - | - | - |
| **RPS** | Unlimited | Rate-limited per plan |
| **TPS** | Unlimited | Shared capacity |
| **Hardware** | Bare-metal, isolated | Multi-tenant servers |
| **Shredstream** | Available (Jito client) | Not available |
| **Uptime SLA** | 99.99% | 99.9% |
| **Client Selection** | Solana Labs, Jito, Frankendancer | Solana Labs only |
| **Custom Config** | Full control | Not available |
| **Support** | Priority 24/7 engineering | Standard support |
## Deployment Timeline
Standard dedicated nodes are provisioned and fully operational within **24-48 hours** of order confirmation. This includes hardware allocation, client installation, configuration, and validation.
Custom configurations, multi-node clusters, and enterprise deployments require **3-5 business days**. This additional time accounts for custom networking, specialized hardware requirements, and tailored configuration tuning.
## Trading APIs on Dedicated Nodes
Dedicated nodes support OrbitFlare's full suite of trading APIs for maximum performance and lowest latency. Access [Metis Swap](/trading-apis/metis-swap) for optimized token swaps and [Jito Bundle Simulation](/trading-apis/jito-bundle-simulation) for MEV bundle testing -- both running directly on your dedicated infrastructure.
## Get Started
Ready to deploy dedicated Solana infrastructure? Reach out to our team on Discord to discuss your requirements, get a custom quote, or schedule a technical consultation.
# FAQs
Source: https://docs.orbitflare.com/faqs
Frequently asked questions about OrbitFlare products and services
## General
OrbitFlare is a high-performance Solana infrastructure provider offering RPC nodes, gRPC streaming, dedicated nodes, and more. All infrastructure is Shredstream-optimized from top turbine validators, delivering the fastest data propagation and lowest latency for your applications.
OrbitFlare supports **Solana** (mainnet and devnet), **BNB Chain (BSC)**, [**Polygon**](/polygon), and **Robinhood Chain**.
OrbitFlare operates across **11 global regions** spanning the United States, Europe, and Asia Pacific. All regions are available on every plan. See the [Global Infrastructure](/infrastructure) page for the full list of locations.
## Billing and Plans
Every OrbitFlare plan includes:
* Devnet endpoint
* WebSocket support
* Access to all 11 global regions
* Historical data (full archive since genesis)
* Shredstream optimization from top turbine validators
* 99.99% uptime SLA
Yes, you can upgrade or downgrade your plan at any time from the dashboard. Upgrades take effect immediately. Downgrades apply at the start of your next billing cycle.
OrbitFlare accepts **credit cards** and **cryptocurrency** (USDC on Solana). Enterprise invoicing is also available for larger accounts.
OrbitFlare offers a free tier with 10 RPS and no time limit. You can use it indefinitely to evaluate the platform before upgrading to a paid plan.
No. OrbitFlare provides unlimited requests within your RPS tier. There are no overage charges and no surprise bills.
OrbitFlare offers three billing periods:
* **Monthly** — Standard pricing
* **Quarterly** — Save 5%
* **Yearly** — Save 15%
## Technical
**Jetstream** is designed for ultra-low latency and is ideal for trading applications where speed is critical. **Yellowstone** provides complete data including inner instructions, making it the better choice for indexing, analytics, and applications that need full transaction detail.
Yes. WebSocket support is included on all plans, enabling real-time subscriptions for account changes, transaction confirmations, and other on-chain events.
Dedicated nodes can be provisioned with the following Solana clients:
* **Solana Labs** — The reference client
* **Jito Labs** — MEV-optimized client
* **Frankendancer** — High-performance validator client
Shredstream is an optimization sourced from top turbine validators that ensures the fastest possible data propagation across the network. It provides a competitive advantage for latency-sensitive applications such as trading bots, arbitrage systems, and real-time data feeds.
Yes. OrbitFlare provides full archive data since Solana genesis (March 2020). Historical data access is included with all RPC plans at no extra cost.
## Support
You can reach OrbitFlare support through:
* **Discord** — [discord.gg/orbitflare](https://discord.gg/orbitflare)
* **Telegram** — [t.me/orbitflare](https://t.me/orbitflare)
* **Email** — [support@orbitflare.com](mailto:support@orbitflare.com)
Response times depend on your plan:
* **Free** — Community support
* **Developer** — Chat support
* **Growth and above** — 24/7 support with under 1 hour response time
* **Dedicated / NFT Pass** — Priority and VIP support
* **Enterprise** — Dedicated account team
Yes. OrbitFlare offers dedicated RPC nodes starting at **2500 USD** per month and dedicated gRPC nodes starting at **1800 USD** per month.
Dedicated infrastructure provides isolated resources, custom configurations, and priority support.
# getTransactionsForAddress
Source: https://docs.orbitflare.com/fetching-data/getTransactionsForAddress
Batch-fetch full Solana transaction history for any address in one JSON-RPC call—pagination, filters, and detail levels replace getSignaturesForAddress plus many getTransaction requests.
## Overview
`getTransactionsForAddress` combines transaction signature lookup and full transaction retrieval into one efficient call. Instead of making separate requests to [getSignaturesForAddress](/rpc/methods/getSignaturesForAddress) and then [getTransaction](/rpc/methods/getTransaction) for each signature, you get everything in a single request.
This method supports:
* Bidirectional sorting (oldest-first or newest-first)
* Time and slot-based filtering via range operators
* Transaction status filtering (succeeded/failed)
* Token account filtering (`none`, `balanceChanged`, `all`)
* Cursor-based pagination via `paginationToken`
* Four detail levels: `signatures`, `none`, `accounts`, `full`
## Why Use This Method
### The N+1 Problem
With standard Solana RPC methods, fetching transaction history requires multiple calls:
1. Call `getSignaturesForAddress` to get a list of transaction signatures
2. Call `getTransaction` for **each** signature to get full transaction data
For a wallet with 100 transactions, that's 101 API calls. For 1,000 transactions, it's 1,001 calls. This creates latency, complexity, and higher costs.
`getTransactionsForAddress` solves this by returning full transaction data in a single call—up to 100 complete transactions or 1,000 signatures per request.
### Additional Capabilities
Beyond efficiency, this method provides features not available in standard RPC:
* **Bidirectional sorting**: Query oldest-first (`asc`) to trace an address from its first transaction
* **Time-based filtering**: Retrieve transactions within a specific date range using `filters.blockTime` with range operators
* **Slot-based filtering**: Query transactions from specific block ranges using `filters.slot` with range operators
* **Status filtering**: Get only successful or only failed transactions
* **Token account inclusion**: Capture transactions from associated token accounts
## Token Account Support
Standard `getSignaturesForAddress` only returns transactions that directly reference the queried address. This misses an important category: **token transfers**.
When tokens are sent to a wallet, the transaction references the wallet's Associated Token Account (ATA), not the wallet address itself. This means standard methods give you an incomplete picture.
`getTransactionsForAddress` with `tokenAccounts: "all"` automatically includes transactions from all token accounts owned by the address, giving you complete transaction history.
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"WALLET_ADDRESS",
{
"filters": {
"tokenAccounts": "all"
}
}
]
}'
```
Set `tokenAccounts: "none"` (the default) if you only want transactions that directly reference the address.
## Parameters
The Solana address to query (base-58 encoded public key, 32 bytes)
Level of detail to return:
* `"signatures"` - Signature info with memo and status (up to 1000 results). **Default.**
* `"none"` - Same fields as `signatures` (up to 1000 results)
* `"accounts"` - Signature info plus account keys (up to 100 results)
* `"full"` - Complete transaction data (up to 100 results)
Default: `"signatures"`
Sort direction:
* `"desc"` - Newest transactions first (default)
* `"asc"` - Oldest transactions first
Maximum number of results to return.
* For `transactionDetails: "signatures"` or `"none"`: max 1000
* For `transactionDetails: "full"` or `"accounts"`: max 100
Default: 100
Pagination cursor from a previous response. Format: `"slot:txIndex"` (e.g. `"387936002:541"`).
The commitment level for the query:
* `"finalized"` - Query finalized blocks only (default)
* `"confirmed"` - Query confirmed blocks
Note: `"processed"` is not supported and returns an error.
Encoding format for transaction data (only applies when `transactionDetails: "full"`):
* `"json"` (default)
* `"jsonParsed"`
* `"base64"`
* `"base58"`
Maximum transaction version to return. In `"full"` mode this defaults to `0` (includes versioned transactions). Set explicitly if you need legacy-only behavior.
The minimum slot that the request can be evaluated at.
Filter by token account association:
* `"none"` - Only transactions that directly reference the address. **Default.**
* `"balanceChanged"` - Include token account transactions where balance changed (recommended for clean wallet history)
* `"all"` - Include all token account transactions
Default: `"none"`
Filter by Unix timestamp using range operators: `gte`, `gt`, `lte`, `lt`, `eq`
Example: `{ "blockTime": { "gte": 1704067200, "lte": 1706745600 } }`
Filter by slot number using range operators: `gte`, `gt`, `lte`, `lt`, `eq`
Example: `{ "slot": { "gte": 387000000, "lte": 388000000 } }`
Filter by transaction signature (base-58, 64 bytes) using range operators: `gte`, `gt`, `lte`, `lt`
Example: `{ "signature": { "lt": "5abc..." } }`
Filter by transaction status:
* `"succeeded"` - Only successful transactions
* `"failed"` - Only failed transactions
* Omit or use any other value for no status filter
## Response
The response `result` is an object containing a `data` array and an optional `paginationToken`:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"data": [ ... ],
"paginationToken": "387936002:541"
}
}
```
The `paginationToken` is only present when more results are available. Pass it in the next request to fetch the next page.
### With `transactionDetails: "signatures"` or `"none"`
Transaction signature (base-58 encoded)
Slot in which the transaction was processed
Zero-based index of the transaction within the block
Unix timestamp of the block
Error object if transaction failed, null otherwise
Memo text if the transaction includes a Memo program instruction
Echoes the requested commitment level (`"finalized"` or `"confirmed"`)
### With `transactionDetails: "accounts"`
Same fields as signatures mode, plus:
Array of base-58 public keys involved in the transaction, including addresses loaded from lookup tables.
### With `transactionDetails: "full"`
Slot in which the transaction was processed
Zero-based index of the transaction within the block. This field is unique to `getTransactionsForAddress`.
Unix timestamp of the block
Full transaction data including message, signatures, and metadata (format depends on `encoding`)
Transaction metadata including fee, balances, and logs
## Code Examples
### Basic Request
Get the most recent signatures for an address (default mode):
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"ADDRESS_HERE"
]
}'
```
### Get Full Transactions
Retrieve up to 100 full transactions:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"ADDRESS_HERE",
{
"transactionDetails": "full",
"limit": 100
}
]
}'
```
### Get Signatures Only
Retrieve up to 1000 signatures without full transaction data:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"ADDRESS_HERE",
{
"transactionDetails": "signatures",
"limit": 1000
}
]
}'
```
### Oldest-First with Time Filter
Get transactions from a specific time range, sorted oldest first:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"ADDRESS_HERE",
{
"sortOrder": "asc",
"filters": {
"blockTime": {
"gte": 1704067200,
"lte": 1706745600
}
}
}
]
}'
```
### Successful Transactions Only
Filter to only return transactions that succeeded:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"ADDRESS_HERE",
{
"filters": {
"status": "succeeded"
}
}
]
}'
```
### Paginated Request
Use cursor-based pagination to fetch more results:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionsForAddress",
"params": [
"ADDRESS_HERE",
{
"limit": 100,
"paginationToken": "387936002:541"
}
]
}'
```
## TypeScript Example
```typescript theme={null}
interface GetTransactionsOptions {
transactionDetails?: 'full' | 'signatures' | 'accounts' | 'none';
limit?: number;
sortOrder?: 'asc' | 'desc';
paginationToken?: string;
commitment?: 'finalized' | 'confirmed';
encoding?: 'json' | 'jsonParsed' | 'base64' | 'base58';
maxSupportedTransactionVersion?: number;
minContextSlot?: number;
filters?: {
tokenAccounts?: 'none' | 'balanceChanged' | 'all';
blockTime?: { gte?: number; gt?: number; lte?: number; lt?: number; eq?: number };
slot?: { gte?: number; gt?: number; lte?: number; lt?: number; eq?: number };
signature?: { gte?: string; gt?: string; lte?: string; lt?: string };
status?: 'succeeded' | 'failed';
};
}
interface GtfaResult {
data: any[];
paginationToken?: string;
}
async function getTransactionsForAddress(
address: string,
options: GetTransactionsOptions = {}
): Promise {
const response = await fetch('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'getTransactionsForAddress',
params: [address, options],
}),
});
const data = await response.json();
return data.result;
}
// Get recent signatures (default mode)
const recent = await getTransactionsForAddress('ADDRESS_HERE');
console.log(recent.data);
// Get oldest transactions first with time filter
const historical = await getTransactionsForAddress('ADDRESS_HERE', {
sortOrder: 'asc',
filters: {
blockTime: {
gte: Math.floor(Date.now() / 1000) - 86400 * 30,
},
},
});
// Paginate through all signatures
async function getAllSignatures(address: string) {
const allSignatures: any[] = [];
let paginationToken: string | undefined;
while (true) {
const result = await getTransactionsForAddress(address, {
transactionDetails: 'signatures',
limit: 1000,
paginationToken,
});
if (!result.data || result.data.length === 0) break;
allSignatures.push(...result.data);
paginationToken = result.paginationToken;
if (!paginationToken) break;
}
return allSignatures;
}
```
## Use Cases
### Token Launch Analysis
Find the earliest transactions for a token mint to identify first minters and early holders. Use `sortOrder: "asc"` to start from the beginning:
```typescript theme={null}
const earlyActivity = await getTransactionsForAddress(tokenMintAddress, {
sortOrder: 'asc',
limit: 100,
transactionDetails: 'full',
filters: {
tokenAccounts: 'all',
},
});
```
### Wallet Funding History
Trace where a wallet's funds originated by querying its complete transaction history:
```typescript theme={null}
const fundingHistory = await getTransactionsForAddress(walletAddress, {
sortOrder: 'asc',
transactionDetails: 'full',
filters: {
tokenAccounts: 'all',
},
});
const firstTransactions = fundingHistory.data.slice(0, 10);
```
### Failed Transaction Analysis
Debug transaction failures by filtering to only failed transactions:
```typescript theme={null}
const result = await getTransactionsForAddress(walletAddress, {
filters: {
status: 'failed',
},
transactionDetails: 'full',
});
result.data.forEach(tx => {
console.log(tx.meta?.err, tx.meta?.logMessages);
});
```
### Build a Wallet Transaction Feed
Create a paginated transaction history for a wallet UI:
```typescript theme={null}
async function getWalletFeed(wallet: string, cursor?: string) {
return getTransactionsForAddress(wallet, {
limit: 20,
paginationToken: cursor,
transactionDetails: 'full',
filters: {
tokenAccounts: 'balanceChanged',
},
});
}
```
### Trading Pattern Analysis
Filter successful trades within a specific time window:
```typescript theme={null}
const trades = await getTransactionsForAddress(traderWallet, {
filters: {
status: 'succeeded',
blockTime: {
gte: startOfMonth,
lt: endOfMonth,
},
},
transactionDetails: 'full',
});
```
### Compliance Audit
Build a complete transaction history for regulatory compliance:
```typescript theme={null}
async function auditAddress(address: string) {
const allTxs = await getAllSignatures(address);
return {
totalTransactions: allTxs.length,
successful: allTxs.filter(tx => !tx.err).length,
failed: allTxs.filter(tx => tx.err).length,
firstTransaction: allTxs[allTxs.length - 1],
lastTransaction: allTxs[0],
};
}
```
## Pagination
For addresses with many transactions, use the `paginationToken` from the response to fetch the next page. The token is a string in the format `"slot:txIndex"` that tells the server where to continue from.
### Fetching Newer Transactions (Default)
With `sortOrder: "desc"` (default), transactions are returned newest-first. The `paginationToken` continues to older transactions:
```typescript theme={null}
const page1 = await getTransactionsForAddress(address, { limit: 100 });
if (page1.paginationToken) {
const page2 = await getTransactionsForAddress(address, {
limit: 100,
paginationToken: page1.paginationToken,
});
}
```
### Fetching Older Transactions First
With `sortOrder: "asc"`, transactions are returned oldest-first. The `paginationToken` continues to newer transactions:
```typescript theme={null}
const page1 = await getTransactionsForAddress(address, {
sortOrder: 'asc',
limit: 100,
});
if (page1.paginationToken) {
const page2 = await getTransactionsForAddress(address, {
sortOrder: 'asc',
limit: 100,
paginationToken: page1.paginationToken,
});
}
```
### Complete Pagination Example
Fetch all signatures for an address:
```typescript theme={null}
async function getAllSignatures(address: string) {
const allSignatures: any[] = [];
let paginationToken: string | undefined;
while (true) {
const result = await getTransactionsForAddress(address, {
transactionDetails: 'signatures',
limit: 1000,
paginationToken,
});
if (!result.data || result.data.length === 0) break;
allSignatures.push(...result.data);
paginationToken = result.paginationToken;
if (!paginationToken) break;
await new Promise(resolve => setTimeout(resolve, 100));
}
return allSignatures;
}
```
## Best Practices
1. **Use `signatures` mode for counting**: When you only need to count or list transactions, use `transactionDetails: "signatures"` (the default) for better performance and higher limits (up to 1000).
2. **Filter early**: Apply `blockTime`, `slot`, or `status` filters to reduce the amount of data scanned and improve response times.
3. **Implement pagination**: For addresses with many transactions, always paginate using the `paginationToken` from the response.
4. **Cache results**: Transaction data is immutable once finalized. Cache historical results to avoid redundant requests.
5. **Handle rate limits**: Implement exponential backoff when making many sequential requests.
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid params | Verify address format (base-58, 32 bytes) and option values |
| -32602 | Invalid paginationToken format | Ensure token is in `"slot:txIndex"` format from a previous response |
| -32602 | commitment must be 'finalized' or 'confirmed' | Do not use `"processed"` commitment |
| -32603 | Internal error | Database or encoding failure; retry the request |
## Comparison with Standard Methods
| Feature | [getSignaturesForAddress](/rpc/methods/getSignaturesForAddress) + [getTransaction](/rpc/methods/getTransaction) | getTransactionsForAddress |
| - | - | - |
| API calls | Multiple (1 + N) | Single |
| Sorting | Newest first only | Bidirectional |
| Time filtering | Not supported | Supported via `filters.blockTime` |
| Slot filtering | Not supported | Supported via `filters.slot` |
| Status filtering | Not supported | Supported via `filters.status` |
| Token accounts | Manual ATA queries | Built-in via `filters.tokenAccounts` |
| Max signatures | 1000 per call | 1000 per call |
| Max full transactions | Limited by calls | 100 per call |
| Pagination | Signature-based `before`/`until` | Cursor-based `paginationToken` |
# Historical Data
Source: https://docs.orbitflare.com/fetching-data/historical-data
Access complete Solana blockchain history from genesis
## Overview
OrbitFlare runs full archive nodes that retain every block, transaction, and account state since Solana launched. Standard RPC providers typically prune older data to save storage, keeping only recent epochs. With OrbitFlare, you can query any slot in Solana's history.
## Use Cases
Historical blockchain data enables several important workflows:
* **Event recovery**: Backfill missed transactions after indexer downtime or webhook failures
* **Compliance**: Retrieve transaction records for audits and regulatory requirements
* **Research**: Analyze on-chain trends, token distributions, and protocol metrics over time
* **Explorer apps**: Build block explorers and dashboards that display complete history
## Relevant RPC Methods
These methods support historical queries on OrbitFlare's archive nodes:
### Block Methods
| Method | Description |
| - | - |
| [getBlock](/rpc/methods/getBlock) | Retrieve full block data for any slot |
| [getBlocks](/rpc/methods/getBlocks) | Get a list of confirmed blocks between two slots |
| [getBlocksWithLimit](/rpc/methods/getBlocksWithLimit) | Get confirmed blocks starting from a slot with a limit |
| [getBlockTime](/rpc/methods/getBlockTime) | Get the estimated Unix timestamp for a block |
| [getBlockHeight](/rpc/methods/getBlockHeight) | Get the current block height |
| [getFirstAvailableBlock](/rpc/methods/getFirstAvailableBlock) | Get the lowest slot with block data available |
### Transaction Methods
| Method | Description |
| - | - |
| [getTransaction](/rpc/methods/getTransaction) | Retrieve a confirmed transaction by signature |
| [getSignaturesForAddress](/rpc/methods/getSignaturesForAddress) | Get transaction signatures for an address |
| [getSignatureStatuses](/rpc/methods/getSignatureStatuses) | Get the status of transaction signatures |
### Account Methods
| Method | Description |
| - | - |
| [getAccountInfo](/rpc/methods/getAccountInfo) | Get account data (current state only) |
| [getMultipleAccounts](/rpc/methods/getMultipleAccounts) | Get data for multiple accounts in one call |
| [getProgramAccounts](/rpc/methods/getProgramAccounts) | Get all accounts owned by a program |
### Slot Methods
| Method | Description |
| - | - |
| [getSlot](/rpc/methods/getSlot) | Get the current slot |
| [minimumLedgerSlot](/rpc/methods/minimumLedgerSlot) | Get the lowest slot the node has data for |
## Examples
### Retrieve a Block from History
Fetch block data for slot 100,000,000 using [getBlock](/rpc/methods/getBlock):
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlock",
"params": [
100000000,
{
"encoding": "json",
"transactionDetails": "full",
"rewards": false,
"maxSupportedTransactionVersion": 0
}
]
}'
```
### Get Block Timestamp
Find when a specific block was produced using [getBlockTime](/rpc/methods/getBlockTime):
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockTime",
"params": [100000000]
}'
```
### List Blocks in a Range
Get all confirmed blocks between two slots using [getBlocks](/rpc/methods/getBlocks):
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlocks",
"params": [100000000, 100000100]
}'
```
### Fetch a Transaction
Retrieve transaction details by signature using [getTransaction](/rpc/methods/getTransaction):
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransaction",
"params": [
"5wHu1qwD7q5menP8rgbzknAHruqg7rq1BwLNjmKnMwVH2VNhVvPLT6jGfKNLwKUTG4Xn2vRvDz5Q6Z8zCLN5gLp1",
{
"encoding": "json",
"maxSupportedTransactionVersion": 0
}
]
}'
```
### Get Transaction History for an Address
Retrieve signatures for an address using [getSignaturesForAddress](/rpc/methods/getSignaturesForAddress):
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSignaturesForAddress",
"params": [
"Vote111111111111111111111111111111111111111",
{"limit": 100}
]
}'
```
## TypeScript Examples
### Fetch Historical Block Data
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY');
async function getHistoricalBlock(slot: number) {
const block = await connection.getBlock(slot, {
maxSupportedTransactionVersion: 0,
});
if (block) {
console.log(`Block ${slot}:`);
console.log(` Transactions: ${block.transactions.length}`);
console.log(` Block time: ${block.blockTime}`);
console.log(` Parent slot: ${block.parentSlot}`);
}
return block;
}
// Fetch a block from early 2023
const block = await getHistoricalBlock(175000000);
```
### Build Complete Transaction History
Combine [getSignaturesForAddress](/rpc/methods/getSignaturesForAddress) with [getTransaction](/rpc/methods/getTransaction) to build a full history:
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY');
async function getFullTransactionHistory(address: string) {
const pubkey = new PublicKey(address);
const allTransactions = [];
let before: string | undefined;
// Paginate through all signatures
while (true) {
const signatures = await connection.getSignaturesForAddress(pubkey, {
limit: 1000,
before,
});
if (signatures.length === 0) break;
// Fetch full transaction data in batches
for (let i = 0; i < signatures.length; i += 10) {
const batch = signatures.slice(i, i + 10);
const txs = await Promise.all(
batch.map(sig =>
connection.getTransaction(sig.signature, {
maxSupportedTransactionVersion: 0,
})
)
);
allTransactions.push(...txs.filter(tx => tx !== null));
}
before = signatures[signatures.length - 1].signature;
}
return allTransactions;
}
```
### Query Blocks in a Time Range
Find blocks within a specific time period:
```typescript theme={null}
async function getBlocksInTimeRange(startTime: number, endTime: number) {
// Get a starting slot estimate
const currentSlot = await connection.getSlot();
const currentTime = Math.floor(Date.now() / 1000);
// Solana produces ~2-3 blocks per second
const slotsPerSecond = 2.5;
const estimatedStartSlot = currentSlot - Math.floor((currentTime - startTime) * slotsPerSecond);
const blocks = [];
let slot = estimatedStartSlot;
while (true) {
const blockTime = await connection.getBlockTime(slot);
if (blockTime && blockTime >= startTime && blockTime <= endTime) {
const block = await connection.getBlock(slot, {
maxSupportedTransactionVersion: 0,
});
if (block) blocks.push({ slot, block });
}
if (blockTime && blockTime > endTime) break;
slot++;
}
return blocks;
}
```
## Performance Tips
1. **Batch your requests**: Use JSON-RPC batch requests to fetch multiple blocks or transactions in one call
2. **Paginate large queries**: The [getSignaturesForAddress](/rpc/methods/getSignaturesForAddress) method returns up to 1000 signatures per call—use the `before` parameter to paginate
3. **Parallelize carefully**: Fetch transactions in parallel batches of 10-20 to balance speed and rate limits
4. **Cache immutable data**: Historical blocks and transactions never change once confirmed—cache them locally
5. **Use `maxSupportedTransactionVersion`**: Always include this parameter to handle versioned transactions
## Limitations
**Account state history**: Solana nodes store current account state, not historical snapshots. You can retrieve transactions that modified an account, but not the account's balance or data at a specific past slot.
**Rate limits**: Historical queries may consume more resources. Check your plan's rate limits and implement appropriate throttling.
## Faster Transaction History
For efficient address history queries, use [getTransactionsForAddress](/fetching-data/getTransactionsForAddress) which combines signature lookup and transaction retrieval into a single call with filtering and sorting options.
## Managed Backfills
For teams that need large-scale historical data delivery without writing custom ETL pipelines, OrbitFlare offers a **Managed Backfills** service.
Managed Backfills is an enterprise service. [Contact the team on Discord](https://discord.gg/orbitflare) or email [sales@orbitflare.com](mailto:sales@orbitflare.com) to discuss your requirements.
### What It Does
OrbitFlare engineers extract and deliver structured Solana historical data directly to your storage:
| Output Format | Supported Destinations |
| - | - |
| JSON (newline-delimited) | AWS S3, Google Cloud Storage |
| Parquet | AWS S3, Google Cloud Storage |
| SQL | PostgreSQL, ClickHouse |
### What Can Be Delivered
* All transactions for a specific program, wallet, or token mint
* Full block data for any slot range
* Token transfer history (SPL Token / Token-2022)
* Custom queries and data shapes based on your requirements
### SLA
| Item | Detail |
| - | - |
| Delivery time | 3–5 business days for standard backfills |
| Data coverage | Genesis (March 2020) to present |
| Format | JSON, Parquet, or SQL — your choice |
| Dedicated support | Assigned engineer for duration of the job |
### When to Use It
Managed Backfills are best for:
* Seeding a new indexer or analytics database
* One-time compliance or audit data requests
* Historical ML/AI training datasets
* Backfilling after extended downtime
For ongoing streaming of new data, use the [gRPC streaming](/data-streaming/yellowstone) endpoints instead.
# Fetching Data
Source: https://docs.orbitflare.com/fetching-data/index
Guides for historical Solana data, wallet and program activity APIs, and bulk transaction retrieval—patterns for analytics and explorers with fewer RPC round-trips on OrbitFlare.
## Overview
OrbitFlare provides powerful tools for retrieving Solana blockchain data. Whether you need to query historical transactions, analyze address activity, or build data pipelines, our infrastructure delivers fast and reliable access to the data you need.
Full archive access from genesis. Query any block, transaction, or account state from Solana's entire history.
Retrieve complete transaction history for any address in a single call with advanced filtering and pagination.
## Why OrbitFlare for Data Access?
* **Full Archive Nodes**: Access complete blockchain history without pruning
* **High Performance**: Optimized infrastructure for fast query responses
* **Developer-Friendly**: Clean APIs that reduce complexity in your codebase
* **Reliable**: Enterprise-grade uptime for production applications
## Coming Soon
We're actively building additional data access features:
* **Digital Asset Standard (DAS) API**: Comprehensive NFT and token metadata
* **Enhanced Transaction Parsing**: Human-readable transaction breakdowns
* **Asset and Token APIs**: Streamlined access to ownership and balance data
Check back for updates or join our [Discord](https://discord.gg/orbitflare) to stay informed.
# Global Infrastructure
Source: https://docs.orbitflare.com/infrastructure
11 strategically distributed server locations across 3 continents
Strategically distributed server locations worldwide
North America, Europe, and Asia Pacific coverage
Enterprise-grade reliability with automatic failover
Ultra-low latency optimized for geographic proximity
## Overview
OrbitFlare operates infrastructure across 11 global regions, optimized with Shredstream from top turbine validators. Endpoints are region-pinned, so you connect directly to the region closest to your deployment for the lowest possible latency.
## Server Locations
### United States (4 locations)
Primary US East hub with direct peering to major exchanges and data centers.
Low-latency access for financial applications and East Coast infrastructure.
West Coast coverage for Pacific-facing applications and services.
Central US routing with high redundancy and low-latency cross-region connectivity.
### Europe (5 locations)
Central European hub with excellent connectivity across the continent.
Major European internet exchange point with broad peering.
Western European coverage with strong financial infrastructure ties.
Northern European presence with robust transatlantic connectivity.
Eastern European coverage for Baltic and Nordic region access.
### Asia Pacific (2 locations)
Primary Asia Pacific hub with low-latency access across East Asia.
Southeast Asian coverage serving one of the fastest-growing blockchain markets.
## Infrastructure Features
Every endpoint is pinned to a region (`fra.rpc.orbitflare.com`, `ams.rpc.orbitflare.com`, ...). Pick the region closest to your infrastructure for the lowest possible latency.
Automatic failover across regions ensures continuous availability. If a node or region experiences issues, traffic is seamlessly redirected to the next closest healthy node.
All nodes are optimized with Shredstream from top turbine validators, ensuring the fastest possible data propagation and a competitive advantage for latency-sensitive applications.
Less than 5ms average response time, optimized for geographic proximity. Our routing layer ensures requests are served by the closest available node to minimize round-trip time.
## Security
OrbitFlare infrastructure is built with enterprise-grade security at every layer:
* **Network Isolation** — Dedicated nodes run in isolated network environments, ensuring your traffic is never shared with other customers.
* **IP Whitelisting** — Restrict access to your endpoints by specifying allowed IP addresses, adding an extra layer of protection.
* **SSL Encryption** — All connections are encrypted with SSL/TLS, securing data in transit across every region.
All 11 regions are available on all plans. Dedicated nodes can be deployed to specific regions based on your requirements.
# OrbitFlare Pass
Source: https://docs.orbitflare.com/nft-pass
OrbitFlare Pass NFT unlocks dedicated RPC, gRPC streaming, and VIP support in one membership—limited 2024 supply; available on secondary markets only.
Only 95 OrbitFlare Passes were ever minted in August 2024. No more will be created. They are now available on secondary markets only.
## Overview
The OrbitFlare Pass is a premium NFT that provides all-access to OrbitFlare's Solana infrastructure. A single pass unlocks dedicated RPC endpoints, gRPC streaming, and VIP support — no separate subscriptions or add-ons required.
Hold the pass in your wallet and you're in. Trade it when you're done.
## Key Benefits
Per-endpoint rate limits designed for high-throughput applications and trading infrastructure, scaling with your access tier.
Access every OrbitFlare RPC region worldwide for the lowest possible latency wherever your users are.
Full access to Jetstream and Yellowstone gRPC streaming at no additional cost.
Priority support with a 4-hour average response time from the infrastructure team.
Be the first to test new features, endpoints, and infrastructure upgrades before public release.
Join an exclusive channel with other pass holders, the engineering team, and direct feedback loops.
The pass is a standard Solana NFT with real utility — buy, sell, or transfer it on any supported marketplace.
## Pricing
Pass holders choose a monthly access tier per endpoint. You can switch tiers at each renewal cycle.
| Tier | Cost | Rate Limits |
| - | - | - |
| **Pro** | \$500 USDC/month | 1000 RPS / 200 TPS |
| **Elite** | \$575 USDC/month | 1250 RPS / 250 TPS |
| **Mega** | \$650 USDC/month | 1500 RPS / 300 TPS |
| **Freeze Option** | \$250 USDC/month | No infrastructure access |
The freeze option keeps your pass active and your slot reserved without paying the full access fee. You won't have access to infrastructure while frozen, but you can reactivate at any time without losing your position.
## Benefits Comparison
| Feature | Standard Plans | NFT Pass |
| - | - | - |
| **RPC Access** | Shared endpoints | Dedicated endpoints |
| **Rate Limits** | Up to 600 RPS / 200 TPS (Pro plan) | Up to 1500 RPS / 300 TPS per endpoint (Mega tier) |
| **gRPC Streaming** | Paid add-on (included on Pro) | Included (Jetstream & Yellowstone) on all tiers |
| **Support Response** | Community to 24/7 priority, by plan | 4-hour average (VIP) |
| **Global Regions** | All 11 regions | All 11 regions |
| **Community** | Public Discord | Private Discord channel |
## Where to Buy
Browse and purchase OrbitFlare Pass listings on Magic Eden.
View floor price and trade OrbitFlare Passes on Tensor.
## Rental Marketplace
Pass holders can rent their node access to other users through the OrbitFlare Discord. If you're not using your full allocation, renting lets you offset your monthly access fee while keeping ownership of the pass.
Contact the team in the private Discord channel for details on setting up a rental arrangement.
# Polygon RPC Nodes
Source: https://docs.orbitflare.com/polygon
Polygon PoS RPC and WebSocket endpoints on OrbitFlare, with plans from 25 to 1000 RPS, gRPC and archival data on every tier, and dedicated instances.
## Overview
OrbitFlare provides Polygon PoS RPC infrastructure for DeFi protocols, gaming, and production applications. Endpoints are region-pinned and authenticated with the same license key as your other OrbitFlare services. Every plan includes RPC, WebSockets, gRPC access, and archival data.
Polygon is EVM-compatible, so the JSON-RPC surface is the standard `eth_*` API.
## Endpoint
| Region | Region Code | Endpoint |
| - | - | - |
| Amsterdam | `ams` | `https://ams.poly.orbitflare.com?api_key=YOUR_API_KEY` |
The general form matches the rest of the platform:
```
https://{region}.poly.orbitflare.com?api_key=YOUR_API_KEY
```
Polygon endpoints are separate from the Solana endpoints documented in [Authentication & Limits](/authentication). Solana uses `{region}.rpc.orbitflare.com`; Polygon uses `{region}.poly.orbitflare.com`.
## Authentication
Append your license key as a query parameter, exactly as with the Solana RPC endpoints:
```bash theme={null}
curl -X POST "https://ams.poly.orbitflare.com?api_key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_blockNumber"
}'
```
Your license key is in the **Licenses** section of your [OrbitFlare Dashboard](https://orbitflare.com/dashboard).
## Making RPC Calls
Polygon uses the same JSON-RPC 2.0 format as Ethereum. Pass `Content-Type: application/json` with a POST body:
```bash cURL theme={null}
curl -X POST "https://ams.poly.orbitflare.com?api_key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://ams.poly.orbitflare.com?api_key=YOUR_API_KEY",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_blockNumber",
params: [],
id: 1,
}),
}
);
const data = await response.json();
console.log("Latest block:", parseInt(data.result, 16));
```
```python Python theme={null}
import requests
response = requests.post(
"https://ams.poly.orbitflare.com?api_key=YOUR_API_KEY",
headers={"Content-Type": "application/json"},
json={
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1,
},
)
data = response.json()
print("Latest block:", int(data["result"], 16))
```
## WebSocket Subscriptions
Real-time subscriptions use the `wss://` scheme with the same host and the same `api_key` parameter:
```javascript theme={null}
const ws = new WebSocket(
"wss://ams.poly.orbitflare.com?api_key=YOUR_API_KEY"
);
ws.on("open", () => {
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_subscribe",
params: ["newHeads"],
}));
});
ws.on("message", (data) => {
const msg = JSON.parse(data);
console.log("New block header:", msg.params?.result);
});
```
## Plans
Throughput scales by plan. RPC, WebSockets, gRPC access, and archival data are included on every tier, and there are no monthly credit or request caps.
| Plan | Price | Requests / Second |
| - | - | :-: |
| Free | \$0/mo | 25 |
| Starter | \$49/mo | 100 |
| Growth | \$149/mo | 300 |
| Pro | \$299/mo | 600 |
| Performance | \$499/mo | 1,000 |
Requests over your plan's RPS limit receive an HTTP `429 Too Many Requests` response. Implement exponential backoff in your client to handle these gracefully.
No. You can send unlimited total requests as long as you stay within your plan's per-second rate.
## Dedicated Instances
For isolated, high-throughput workloads, OrbitFlare also offers dedicated Polygon nodes:
| Feature | Details |
| - | - |
| **Throughput** | Custom, unmetered |
| **Hardware** | 100% dedicated resources, isolated environment |
| **Configuration** | Custom node configs and parameters |
| **SLA** | Custom uptime and response terms |
| **Support** | Priority engineering support |
| **Pricing** | Custom quote based on throughput, region, and SLA |
Dedicated Polygon infrastructure is quoted per deployment. [Contact sales](mailto:sales@orbitflare.com) to discuss throughput, region, and SLA requirements.
## Supported Methods
Polygon endpoints expose the standard EVM JSON-RPC API, including:
| Category | Methods |
| - | - |
| **Blocks** | `eth_blockNumber`, `eth_getBlockByHash`, `eth_getBlockByNumber` |
| **Transactions** | `eth_sendRawTransaction`, `eth_getTransactionByHash`, `eth_getTransactionReceipt` |
| **Accounts** | `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getTransactionCount` |
| **Contracts** | `eth_call`, `eth_estimateGas`, `eth_getLogs` |
| **Network** | `net_version`, `eth_chainId`, `eth_gasPrice`, `eth_feeHistory` |
| **WebSocket** | `eth_subscribe` (newHeads, logs, newPendingTransactions) |
Polygon PoS uses `chainId: 137` for mainnet and `chainId: 80002` for the Amoy testnet.
## Rate Limits and Quotas
These limits apply to all OrbitFlare EVM chains (Robinhood, Polygon, BSC). They are enforced per license, and HTTP and WebSocket traffic on the same key share a single rate limit and in-flight budget.
| Control | Free | Paid |
| - | - | - |
| Requests / second | plan RPS | plan RPS |
| Batch size (HTTP + WS) | 1 item | 100 items |
| `eth_getLogs` in flight | 1 | 10 |
| `eth_getLogs` block range | 150 blocks | 10,000 blocks |
| `debug_*` / `trace_*` methods | blocked | allowed |
| WebSocket connections | 1 | from 10 |
| Subscriptions per WebSocket connection | 5 | 25 |
| `logs` subscription without a filter | refused | allowed |
| WebSocket data per connection | 1 GiB | unlimited |
Applies to both tiers: WebSocket connection lifetime of 24 hours; idle timeout of 30 minutes with no data frames; frame size of 4 MiB inbound and 32 MiB outbound; each batch item counts as one request.
### Error Codes
| HTTP | JSON-RPC | Meaning |
| - | - | - |
| 429 | `-32429` | Rate limit exceeded |
| 429 | `-32005` | Concurrent, batch, connection, or subscription limit exceeded |
| 400 | `-32602` | `eth_getLogs` block range too large (the message names the span) |
| 403 | `-32601` | Method not available on this plan |
| 403 | `-32406` | WebSockets not enabled for this key |
`-32005` and `-32429` are transient. Retry with exponential backoff. If `-32005` persists on isolated calls, another client is likely using the same key concurrently, since HTTP and WebSocket share one in-flight budget.
## Get Started
How API keys work, endpoint formats, rate limits, and error semantics.
Talk to the OrbitFlare team about dedicated Polygon instances and enterprise terms.
# Products & Pricing
Source: https://docs.orbitflare.com/products
Compare OrbitFlare Solana RPC, Shredstream, dedicated nodes, data APIs, staking, and Pass pricing. Shredstream-optimized RPC with transparent limits, SLAs, and global regions.
High-performance Solana infrastructure optimized with Shredstream from top turbine validators.
Uptime SLA
Global Regions
Avg Response Time
Expert Support
***
## OrbitFlare Pass
**Premium all-access pass** — Only 95 ever minted. Available on secondary markets.
**\$650**/month
**1500 RPS** per endpoint**300 TPS** per endpoint
* All 11 global RPC regions
* gRPC streaming included (Jetstream & Yellowstone)
* VIP support (4-hour avg response)
* Early access to new features
* Private Discord community
**Buy on:** [Magic Eden](https://magiceden.io/marketplace/orbitflare-pass) · [Tensor](https://tensor.trade/collection/orbitflare)
[Learn more about the OrbitFlare Pass →](/nft-pass)
***
## RPC Plans
No credit limits—unlimited requests within your RPS tier.
**\$0**/month
* 10 RPS / 1 TPS
* Devnet gRPC only
* Community support
* WebSockets included
* 11 global regions
**\$49**/month
* 50 RPS / 10 TPS
* Devnet gRPC only
* Chat support
* WebSockets included
* 11 global regions
**\$399**/month
* 200 RPS / 75 TPS
* gRPC add-on (+\$500)
* 24/7 support
* WebSockets included
* 11 global regions
**\$799**/month
* 400 RPS / 150 TPS
* gRPC add-on (+\$500)
* 24/7 priority support
* WebSockets included
* 11 global regions
* Historical data included
**\$999**/month
* 600 RPS / 200 TPS
* gRPC **included**
* 24/7 priority support
* WebSockets included
* 11 global regions
* Historical data included
**\$0**/quarter
* 10 RPS / 1 TPS
* Devnet gRPC only
* Community support
**\$139**/quarter
* 50 RPS / 10 TPS
* Devnet gRPC only
* Chat support
**\$1,137**/quarter
* 200 RPS / 75 TPS
* gRPC add-on available
* 24/7 support
**\$2,277**/quarter
* 400 RPS / 150 TPS
* gRPC add-on available
* 24/7 priority support
**\$2,847**/quarter
* 600 RPS / 200 TPS
* gRPC **included**
* 24/7 priority support
**\$0**/year
* 10 RPS / 1 TPS
* Devnet gRPC only
* Community support
**\$499**/year
* 50 RPS / 10 TPS
* Devnet gRPC only
* Chat support
**\$4,069**/year
* 200 RPS / 75 TPS
* gRPC add-on available
* 24/7 support
**\$8,149**/year
* 400 RPS / 150 TPS
* gRPC add-on available
* 24/7 priority support
**\$10,189**/year
* 600 RPS / 200 TPS
* gRPC **included**
* 24/7 priority support
**All plans include:** Devnet endpoint, WebSockets, 11 global regions, historical data access, Shredstream optimization, and 99.99% uptime SLA.
Choose your plan and start building today
***
## gRPC Streaming
Real-time Solana data via **Jetstream** (ultra-low latency) or **Yellowstone** (complete data).
**\$500**/month
* Unlimited real-time streaming
* Shared resources (99.99% uptime)
* Full gRPC access (1 IP)
* Jetstream & Yellowstone
**Best for:** Developers, small bots
**\$1,800**/month
* Dedicated hardware
* Unlimited streaming
* Optimized for HFT
* Priority support
**Best for:** HFT, analytics platforms
| Feature | Jetstream | Yellowstone |
| - | - | - |
| **Latency** | Ultra-low | Low |
| **Inner instructions** | No | Yes |
| **Transaction metadata** | No | Yes |
| **Best for** | Real-time trading | Indexing & analytics |
| **Connection type** | Shreds-based | Geyser plugin |
Learn more in our [Data Streaming documentation](/data-streaming/jetstream).
***
## Dedicated Nodes
Enterprise-grade infrastructure with unlimited performance.
**Starting at \$2,500**/month
* Unlimited RPS & TPS
* 100% dedicated hardware
* Choice of client (Labs, Jito, Frankendancer)
* Custom configuration
* Priority support + account manager
* 99.99% uptime SLA
**Starting at \$1,800**/month
* Dedicated streaming resources
* Unlimited real-time data
* Optimized for HFT & analytics
* Priority support
Need multiple nodes, custom SLAs, or on-premise deployment? **Contact our sales team.**
***
## Historical Data
Complete Solana blockchain history since genesis (March 2020). **Included with all RPC plans at no extra cost.**
* \<10ms P50 query latency
* Global redundancy across 11 regions
* Standard RPC methods supported
* Managed backfills available for enterprise
[Learn more →](/fetching-data/historical-data)
***
## Getting Started
Select the tier that matches your RPS and feature needs. Start free to test.
Sign up at [orbitflare.com](https://orbitflare.com) and complete checkout.
Access your dashboard to retrieve your API credentials.
Use our RPC endpoints in your application. Check our [quickstart guide](/quickstart).
***
## Why OrbitFlare?
Powered by top turbine validators for fastest data propagation.
US, Europe, and Asia Pacific coverage for low latency worldwide.
Unlimited requests within your RPS. No surprise bills.
Enterprise reliability with automatic failover.
Solana-native engineers via Discord, Telegram, email.
Full archive from genesis included with all plans.
***
## FAQs
All plans include: Devnet endpoint, WebSocket support, access to all 11 global regions, historical data access, Shredstream optimization, and 99.99% uptime SLA.
Yes, you can change your plan at any time from your dashboard. Upgrades take effect immediately, and downgrades apply at the next billing cycle.
We accept credit cards and cryptocurrency (USDC on Solana). Enterprise customers can arrange invoicing.
Yes! Our Free tier gives you 10 RPS with no time limit. Use it to test our infrastructure before upgrading.
Jetstream provides ultra-low latency streaming optimized for trading. Yellowstone provides complete transaction data including inner instructions, better for indexing and analytics.
Yes, dedicated RPC nodes start at $2,500/month and dedicated gRPC nodes at $1,800/month. Contact us for enterprise custom solutions.
***
## More Products
Stake SOL with our high-performance validator. 5-7% APY with JITO MEV rewards.
Dedicated BNB/BSC node infrastructure starting at \$1,800/month.
11 regions across US, Europe, and Asia Pacific.
***
Ready to get started? [View pricing](https://orbitflare.com/pricing) or [contact our team](https://discord.gg/orbitflare) to discuss your requirements.
# Quickstart
Source: https://docs.orbitflare.com/quickstart
Create your OrbitFlare account, copy your license key from the dashboard, and call mainnet Solana JSON-RPC with curl or any HTTP client. Includes a free tier; upgrade for higher limits and regional endpoints.
Sign up at [orbitflare.com](https://orbitflare.com) and choose a plan that fits your needs. A **free tier** is available so you can start building immediately with no credit card required.
The free plan includes 10 requests per second -- enough to prototype and test your integration before upgrading.
Once you have signed in, navigate to the **Dashboard** and locate the **Licenses** section. Your license key is provided with your service subscription — copy it from there.
You will authenticate RPC requests by appending the license key as the `api_key` query parameter to the endpoint URL.
Keep your license key secret. Do not commit it to version control or expose it in client-side code.
Use the mainnet endpoint to call `getBlockHeight` and confirm everything is working.
```bash cURL theme={null}
curl -X POST "https://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockHeight"
}'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "getBlockHeight",
}),
}
);
const data = await response.json();
console.log("Block height:", data.result);
```
```python Python theme={null}
import requests
response = requests.post(
"https://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY",
headers={"Content-Type": "application/json"},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockHeight",
},
)
data = response.json()
print("Block height:", data["result"])
```
A successful response looks like this:
```json theme={null}
{
"jsonrpc": "2.0",
"result": 283458923,
"id": 1
}
```
OrbitFlare supports persistent WebSocket connections for real-time data. Connect to `wss://fra.rpc.orbitflare.com` and subscribe to updates.
```javascript JavaScript theme={null}
const WebSocket = require("ws");
const ws = new WebSocket(
"wss://fra.rpc.orbitflare.com?api_key=YOUR_API_KEY"
);
ws.on("open", () => {
ws.send(
JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "slotSubscribe",
})
);
console.log("Subscribed to slot updates");
});
ws.on("message", (data) => {
const message = JSON.parse(data);
console.log("Received:", message);
});
ws.on("error", (err) => {
console.error("WebSocket error:", err);
});
```
WebSocket connections use the same `api_key` query parameter for authentication.
## What's Next?
Understand API key usage, endpoint formats, rate limits, and streaming connection limits.
Dive into the complete HTTP RPC method reference.
Stream real-time Solana data with Jetstream.
HTTP status codes, JSON-RPC errors, and how to handle them.
Production-ready starter templates for building on Solana — Blinks, swaps, and more.
# Robinhood Chain Nodes
Source: https://docs.orbitflare.com/robinhood-chain
Dedicated Robinhood Chain node access from OrbitFlare — low-latency RPC and WebSocket endpoints for the tokenized-stock L2, available for purchase from the OrbitFlare dashboard.
## Overview
OrbitFlare provides node infrastructure for **Robinhood Chain**, the EVM-compatible Ethereum Layer 2 that Robinhood launched on mainnet in July 2026. The chain is built on the Arbitrum Orbit (Nitro) stack, uses ETH for gas, and is home to Robinhood's tokenized stocks, which trade around the clock and integrate with DeFi protocols such as Uniswap and Chainlink from day one.
If you are building trading systems, indexers, or DeFi integrations around tokenized equities, OrbitFlare gives you direct node access without operating Nitro infrastructure yourself. Because the chain is fully EVM-compatible, standard tooling like Hardhat, Foundry, ethers.js, viem, and Wagmi works against the endpoint without modification, and the chain has first-class support for ERC-4337 account abstraction.
Robinhood Chain node access is available for purchase directly from the [OrbitFlare dashboard](https://orbitflare.com/dashboard). Learn more on the [product page](https://orbitflare.com/products/robinhood-chain-rpcs).
Robinhood Chain access is also included with the [OrbitFlare Pass](/nft-pass) NFT, capped at 100 RPS for Pass holders and renters.
## Endpoints
Robinhood Chain mainnet runs in two regions. Use the endpoint closest to your servers.
| Region | JSON-RPC (HTTP) | WebSocket |
| - | - | - |
| **US (Ohio)** | `https://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY` | `wss://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
| **Amsterdam (EU)** | `https://ams.robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY` | `wss://ams.robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY` |
Robinhood Chain mainnet uses `chainId: 4663` with ETH as the gas token.
### Access
Authenticate with your OrbitFlare license key, passed as the `?api_key=` query parameter on both the HTTP and WebSocket URLs (the same key format, `ORBIT-XXXXXX-NNNNNN-NNNNNN`, used across OrbitFlare products). A license can optionally be placed in IP-whitelist mode, where only whitelisted source IPs may connect and requests still carry the key; manage the mode and whitelist from the [dashboard](https://orbitflare.com/dashboard).
Robinhood Chain also runs a public testnet (`chainId: 46630`), an Arbitrum Orbit chain settling to Ethereum Sepolia with ETH as the gas token:
| Resource | Location |
| - | - |
| **Faucet** | [faucet.testnet.chain.robinhood.com](https://faucet.testnet.chain.robinhood.com) |
| **Explorer** | [explorer.testnet.chain.robinhood.com](https://explorer.testnet.chain.robinhood.com) |
| **Bridge (from Sepolia)** | [Arbitrum Bridge](https://portal.arbitrum.io/bridge?destinationChain=robinhood-chain-testnet\&sourceChain=sepolia) |
Generate a throwaway keypair, request testnet ETH from the faucet, and you can exercise the full transaction lifecycle end to end.
The OrbitFlare endpoint serves **mainnet only**.
## Making RPC Calls
Robinhood Chain is EVM-compatible and speaks standard JSON-RPC 2.0. Pass `Content-Type: application/json` with a POST body:
```bash cURL theme={null}
curl -X POST "https://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
```
```typescript viem theme={null}
import { createPublicClient, defineChain, http } from "viem";
const robinhoodChain = defineChain({
id: 4663,
name: "Robinhood Chain",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["https://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY"] },
},
});
const client = createPublicClient({
chain: robinhoodChain,
transport: http(),
});
const block = await client.getBlockNumber();
console.log("Latest block:", block);
```
```python web3.py theme={null}
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY"))
print("Connected:", w3.is_connected())
print("Chain ID:", w3.eth.chain_id) # 4663
print("Latest block:", w3.eth.block_number)
```
### WebSocket Subscriptions
Real-time subscriptions run over the WebSocket endpoint. In Node.js, install the `ws` package (`npm install ws`):
```javascript theme={null}
import WebSocket from "ws";
const ws = new WebSocket("wss://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY");
ws.on("open", () => {
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_subscribe",
params: ["newHeads"],
}));
});
ws.on("message", (data) => {
const msg = JSON.parse(data);
console.log("New block header:", msg.params?.result);
});
```
Robinhood Chain produces blocks roughly every 100 milliseconds, so expect a much faster `newHeads` cadence than on Ethereum mainnet or most L2s.
### Supported Methods
The node exposes the standard EVM JSON-RPC surface, including:
| Category | Methods |
| - | - |
| **Blocks** | `eth_blockNumber`, `eth_getBlockByHash`, `eth_getBlockByNumber` |
| **Transactions** | `eth_sendRawTransaction`, `eth_getTransactionByHash`, `eth_getTransactionReceipt` |
| **Accounts** | `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getTransactionCount` |
| **Contracts** | `eth_call`, `eth_estimateGas`, `eth_getLogs` |
| **Network** | `net_version`, `eth_chainId`, `eth_gasPrice`, `eth_feeHistory` |
| **WebSocket** | `eth_subscribe` (newHeads, logs, newPendingTransactions) |
As an Arbitrum Nitro chain, the node also serves the `arb_*` extension methods alongside the standard `eth_*` set. For the full method reference, see the [Ethereum JSON-RPC specification](https://ethereum.org/en/developers/docs/apis/json-rpc/).
## Rate Limits and Quotas
These limits apply to all OrbitFlare EVM chains (Robinhood, Polygon, BSC). They are enforced per license, and HTTP and WebSocket traffic on the same key share a single rate limit and in-flight budget.
| Control | Free | Paid |
| - | - | - |
| Requests / second | plan RPS | plan RPS |
| Batch size (HTTP + WS) | 1 item | 100 items |
| `eth_getLogs` in flight | 1 | 10 |
| `eth_getLogs` block range | 150 blocks | 10,000 blocks |
| `debug_*` / `trace_*` methods | blocked | allowed |
| WebSocket connections | 1 | from 10 |
| Subscriptions per WebSocket connection | 5 | 25 |
| `logs` subscription without a filter | refused | allowed |
| WebSocket data per connection | 1 GiB | unlimited |
Applies to both tiers: WebSocket connection lifetime of 24 hours; idle timeout of 30 minutes with no data frames; frame size of 4 MiB inbound and 32 MiB outbound; each batch item counts as one request.
### Error Codes
| HTTP | JSON-RPC | Meaning |
| - | - | - |
| 429 | `-32429` | Rate limit exceeded |
| 429 | `-32005` | Concurrent, batch, connection, or subscription limit exceeded |
| 400 | `-32602` | `eth_getLogs` block range too large (the message names the span) |
| 403 | `-32601` | Method not available on this plan |
| 403 | `-32406` | WebSockets not enabled for this key |
`-32005` and `-32429` are transient. Retry with exponential backoff. If `-32005` persists on isolated calls, another client is likely using the same key concurrently, since HTTP and WebSocket share one in-flight budget.
## Example: Reading a Tokenized Stock
The chain's flagship assets are Robinhood's tokenized stocks, which are standard ERC-20 contracts. Here is the NVIDIA token (`NVDA`) read directly from chain state:
```typescript viem theme={null}
import { createPublicClient, defineChain, erc20Abi, http } from "viem";
const robinhoodChain = defineChain({
id: 4663,
name: "Robinhood Chain",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["https://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY"] },
},
});
const client = createPublicClient({ chain: robinhoodChain, transport: http() });
const NVDA = "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC";
const [name, symbol, decimals, totalSupply] = await Promise.all([
client.readContract({ address: NVDA, abi: erc20Abi, functionName: "name" }),
client.readContract({ address: NVDA, abi: erc20Abi, functionName: "symbol" }),
client.readContract({ address: NVDA, abi: erc20Abi, functionName: "decimals" }),
client.readContract({ address: NVDA, abi: erc20Abi, functionName: "totalSupply" }),
]);
console.log(`${name} (${symbol}), ${totalSupply / 10n ** BigInt(decimals)} shares tokenized`);
// NVIDIA • Robinhood Token (NVDA), 26419 shares tokenized
```
```bash cURL theme={null}
# symbol() on the NVDA token contract
curl -X POST "https://robinhood.rpc.orbitflare.com?api_key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC",
"data": "0x95d89b41"
}, "latest"],
"id": 1
}'
```
Search the [explorer](https://robinhoodchain.blockscout.com) carefully before hardcoding token addresses: many copycat tokens use the `NVDA` symbol. The official Robinhood stock tokens carry the `• Robinhood Token` naming, and the address above is the one held by tens of thousands of accounts.
## Use Cases
Trade and monitor on-chain equities like NVDA and AAPL 24/7, without market-hours limits.
Build against Uniswap pools and Chainlink feeds live on the chain since launch.
Follow 100ms blocks in real time over WebSocket and keep your own view of chain state.
## Get Started
Buy Robinhood Chain node access directly from the OrbitFlare dashboard and grab your license key.
Typed RPC and WebSocket clients for Robinhood Chain, built on alloy with retry and failover out of the box.
Learn more about Robinhood Chain RPC access, pricing, and what's included.
## Official Resources
* [Robinhood Chain documentation](https://docs.robinhood.com/chain)
* [Robinhood Chain explorer (Blockscout)](https://robinhoodchain.blockscout.com)
* [Robinhood Chain status page](https://status.robinhoodchain.offchain.io)
# Common Error Codes
Source: https://docs.orbitflare.com/rpc/error-codes
Understanding RPC error messages and their solutions
When interacting with OrbitFlare RPC endpoints, you may encounter the following standard HTTP error codes. This guide helps you understand what these errors mean and how to resolve them.
## HTTP Status Codes
| Error Code | Error Message | Description | Solution |
| - | - | - | - |
| 401 | Unauthorized | Invalid API key or authentication token. | - Verify your API key is correct - Check if your API key has the required permissions - Ensure your API key is properly set in the request headers |
| 403 | Forbidden | Access denied due to insufficient permissions or IP restrictions. | - Verify your IP is whitelisted if IP restrictions are enabled - Check if your subscription plan allows the requested operation |
| 429 | Too Many Requests | You've exceeded the rate limits for your subscription tier. | - Implement request rate limiting in your application - Consider upgrading to a higher tier with increased limits - Check your current usage in the dashboard |
| 500 | Internal Server Error | An unexpected error occurred on our servers. | - Retry the request after a short delay - If the issue persists, contact our support team |
| 502 | Bad Gateway | Temporary issue with our upstream services. | - Retry the request after a few moments - If persistent, check our status page for any ongoing incidents |
| 503 | Service Unavailable | Service is temporarily unavailable or under maintenance. | - Retry with exponential backoff - Check our status page for maintenance announcements |
| 504 | Gateway Timeout | Request timed out while waiting for a response. | - Check your network connection - Consider reducing the complexity of your request - Try breaking large requests into smaller chunks |
## JSON-RPC Errors
| Error Code | Message | Description | Solution |
| - | - | - | - |
| -32700 | Parse error | Invalid JSON received | Check your request format and ensure it's valid JSON |
| -32600 | Invalid request | JSON is valid but request format is wrong | Verify your request matches the RPC method specification |
| -32601 | Method not found | The requested method doesn't exist | Check the method name and our API documentation |
| -32602 | Invalid params | Method parameters are invalid | Review the parameters required for the method |
| -32603 | Internal error | Internal JSON-RPC error | Retry the request; if persistent, contact support |
| -32000 | Server error | Generic server-side error | Retry with exponential backoff |
| -32001 | Transaction simulation failed | Transaction failed during simulation | Check transaction parameters and account states |
| -32002 | Account not found | Requested account doesn't exist | Verify the account address is correct |
| -32003 | Block not found | Requested block not available | Check if the block is within available history |
| -32004 | Node unhealthy | RPC node is not in a healthy state | Try a different RPC endpoint |
## Best Practices
1. **Implement Retry Logic**
* Use exponential backoff for retries
* Set appropriate timeout values
* Handle rate limits gracefully
2. **Error Handling**
* Always check error responses
* Log error details for debugging
* Implement proper fallback mechanisms
3. **Monitoring**
* Monitor error rates in your application
* Set up alerts for unusual error patterns
* Track API usage and rate limit consumption
## Support
If you encounter persistent issues or need help understanding specific errors, please:
1. Check our [status page](https://status.orbitflare.com) for system status
2. Join our [Discord community](https://discord.gg/orbitflare) for quick help
3. Contact our support team for detailed assistance
For real-time updates on system status and maintenance, follow us on [Twitter](https://twitter.com/orbitflarenodes).
# HTTP RPC API
Source: https://docs.orbitflare.com/rpc/http
Interact with Solana nodes using JSON-RPC 2.0
## Overview
OrbitFlare RPC nodes accept HTTP requests using the JSON-RPC 2.0 specification. This API allows you to interact with the Solana blockchain for querying data and submitting transactions.
## Request Format
To make a JSON-RPC request, send an HTTP POST request with the following specifications:
* Endpoint: `https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY`
* Header: `Content-Type: application/json`
* Body: JSON object with these fields:
| Field | Type | Description |
| - | - | - |
| `jsonrpc` | string | Must be "2.0" |
| `id` | string/number | Unique request identifier |
| `method` | string | The RPC method name |
| `params` | array | Array of method parameters |
### Example Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
]
}'
```
### Response Format
Responses will be JSON objects containing:
| Field | Type | Description |
| - | - | - |
| `jsonrpc` | string | Always "2.0" |
| `id` | string/number | Matches request identifier |
| `result` | varies | The method's result data |
| `error` | object | Error details (if applicable) |
## Commitment
Many methods accept a `commitment` parameter to specify how confirmed the data should be:
* `processed`: Latest block (fastest, not confirmed)
* `confirmed`: Confirmed by supermajority (balance between speed and finality)
* `finalized`: Finalized by supermajority (slowest, fully confirmed)
Example with commitment:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",
{"commitment": "finalized"}
]
}'
```
## Batch Requests
You can send multiple requests in a single HTTP call by providing an array of request objects:
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY -X POST -H "Content-Type: application/json" -d '[
{
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": ["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]
},
{
"jsonrpc": "2.0",
"id": 2,
"method": "getBlockHeight",
"params": []
}
]'
```
## Common Types
| Type | Description |
| - | - |
| Pubkey | Base-58 encoded public key string |
| Hash | Base-58 encoded SHA-256 hash string |
| Signature | Base-58 encoded Ed25519 signature |
| Slot | Integer block height/slot number |
## Health Check
You can check node health with a GET request:
```bash theme={null}
curl "https://fra.rpc.orbitflare.com/health?api_key=YOUR_LICENSE_KEY"
```
Possible responses:
* `ok`: Node is healthy and up-to-date
* `behind { slots: number }`: Node is behind by N slots
* `error`: Node is unhealthy
## Available Methods
* getAccountInfo
* getMultipleAccounts
* getProgramAccounts
* getMinimumBalanceForRentExemption
* getBlock
* getBlocks
* getBlockHeight
* getSlot
* getSlotLeader
* getTransaction
* getSignatureStatuses
* getSignaturesForAddress
* sendTransaction
* simulateTransaction
* getTokenAccountBalance
* getTokenAccountsByDelegate
* getTokenAccountsByOwner
* getTokenSupply
## Rate Limits
Rate limits vary by subscription tier. See the [Authentication & Limits](/authentication#rate-limits-by-plan) page for the exact RPS and TPS values per plan.
## Best Practices
1. **Use Appropriate Commitment Levels**
* Use `processed` for UI updates
* Use `confirmed` for most operations
* Use `finalized` for critical operations
2. **Optimize Requests**
* Use batch requests when possible
* Implement proper caching
* Choose appropriate polling intervals
3. **Handle Errors**
* Implement proper error handling
* Use exponential backoff for retries
* Monitor request failures
## See Also
API key setup, endpoint formats, rate limits, and connection limits.
HTTP status codes, JSON-RPC errors, and how to handle them.
Real-time subscriptions for accounts, slots, logs, and more.
## Support
For technical support or questions about our RPC service:
* Join our [Discord community](https://discord.gg/orbitflare)
* Check our [status page](https://status.orbitflare.com)
* Contact our support team
# getAccountInfo
Source: https://docs.orbitflare.com/rpc/methods/getAccountInfo
Returns all information associated with an account
Returns all information associated with the account of provided public key.
## Parameters
The public key of the account to query (base-58 encoded string)
Configuration object containing the following optional fields:
The level of commitment to use for querying the network:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
Encoding format for the account data:
* `base58`
* `base64`
* `base64+zstd`
* `jsonParsed` (limited to certain account types)
Request a slice of the account's data:
* `offset`: number - Start position (in bytes)
* `length`: number - Number of bytes to return
The minimum slot that the request can be evaluated at
## Response
If the requested account doesn't exist, returns `null`. Otherwise, returns an object containing:
The slot the request was processed at
Number of lamports assigned to the account
Base-58 encoded public key of the program that owns this account
Whether this account contains a program
The epoch at which this account will next owe rent
The account's data. Format depends on the encoding parameter:
* `[string, encoding]` tuple if encoded as base58/base64
* Parsed JSON object if encoding is "jsonParsed"
The space used by the account's data
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getAccountInfo",
"params": [
"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg",
{
"encoding": "base58"
}
]
}'
```
### Response
```json theme={null}
{
"jsonrpc": "2.0",
"result": {
"context": {
"slot": 430
},
"value": {
"data": ["", "base58"],
"executable": false,
"lamports": 5000000000,
"owner": "11111111111111111111111111111111",
"rentEpoch": 18446744073709551615,
"space": 0
}
},
"id": 1
}
```
### Request with Data Slice
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getAccountInfo",
"params": [
"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg",
{
"encoding": "base64",
"dataSlice": {
"offset": 0,
"length": 64
}
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const publicKey = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg');
const accountInfo = await connection.getAccountInfo(
publicKey,
'confirmed'
);
```
## Notes
1. The `jsonParsed` encoding is only available for certain account types:
* Stake account
* Token account
* Token mint
* Token metadata
2. When using `dataSlice`, the data field will be limited to the requested slice only.
3. The account data may be encoded differently based on the program that owns the account.
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid param: WrongSize | Verify the public key is valid |
| -32602 | Invalid param: not base58 encoded string | Ensure the public key is base58 encoded |
| -32007 | Account not found | The requested account does not exist |
# getBalance
Source: https://docs.orbitflare.com/rpc/methods/getBalance
Returns the balance of the account of provided public key
## Parameters
Public key of the account to query (base-58 encoded string)
Configuration object containing the following optional fields:
The level of commitment to use:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
The minimum slot that the request can be evaluated at
## Response
The slot the request was processed at
Number of lamports assigned to this account
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
]
}'
```
### Request with Commitment
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",
{
"commitment": "finalized"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const publicKey = new PublicKey('83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri');
const balance = await connection.getBalance(
publicKey,
'confirmed'
);
console.log(`Balance: ${balance / 1e9} SOL`);
```
## Notes
1. Balance is returned in lamports (1 SOL = 1,000,000,000 lamports)
2. For new accounts, balance will be 0
3. The balance includes all SOL tokens, including those delegated for staking
4. The response is immediate as it reads from the current state
## Best Practices
1. Use appropriate commitment level based on your needs:
* `processed` for UI updates
* `confirmed` for most operations
* `finalized` for critical operations
2. Convert lamports to SOL by dividing by 1e9 when displaying to users
3. Consider using `getAccountInfo` if you need more account details
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid param: WrongSize | Verify the public key is valid |
| -32602 | Invalid param: not base58 encoded string | Ensure the public key is base58 encoded |
| -32007 | Account not found | The account doesn't exist (balance will be 0) |
# getBlock
Source: https://docs.orbitflare.com/rpc/methods/getBlock
Returns identity and transaction information about a confirmed block
## Parameters
The slot number of the block to retrieve
Configuration object containing the following optional fields:
Encoding format for transaction data:
* `json` - JSON format
* `jsonParsed` - JSON format with parsed transaction data
* `base58` - Base-58 encoded binary data
* `base64` - Base-64 encoded binary data
Level of transaction detail to return:
* `full` - Full transaction data
* `signatures` - Only transaction signatures
* `none` - No transaction data
Whether to populate the rewards array
Bank state to query:
* `processed` - Latest block (unconfirmed)
* `confirmed` - Confirmed by supermajority
* `finalized` - Finalized by supermajority
The maximum transaction version to return in responses
## Response
Returns `null` if the block is not found. Otherwise, returns an object containing:
The blockhash of this block (base-58)
The blockhash of the previous block (base-58)
The slot index of the parent block
Array of transaction information (if `transactionDetails` != "none"):
* For `full`: Complete transaction objects
* For `signatures`: Only transaction signatures
Array of reward objects (if `rewards` = true):
* `pubkey`: string - The public key (base-58)
* `lamports`: number - Number of reward lamports
* `postBalance`: number - Account balance after reward
* `rewardType`: string - Type of reward
* `commission`: number - Vote account commission (vote only)
Estimated production time (Unix timestamp)
The number of blocks beneath this block
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlock",
"params": [
430,
{
"encoding": "json",
"transactionDetails": "full",
"rewards": true
}
]
}'
```
### Request with Parsed Transaction Data
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlock",
"params": [
430,
{
"encoding": "jsonParsed",
"transactionDetails": "full",
"rewards": true,
"maxSupportedTransactionVersion": 0
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const slot = 430;
const block = await connection.getBlock(
slot,
{
maxSupportedTransactionVersion: 0
}
);
```
## Notes
1. Block production time is an estimate based on genesis block time and slots elapsed.
2. Not all blocks include rewards.
3. The `jsonParsed` encoding attempts to parse transaction instruction data based on known program layouts.
4. Some blocks might be skipped (no leader assigned or failed block production).
5. Block data may be pruned from the node based on ledger configuration.
## Best Practices
1. Use `transactionDetails: "signatures"` if you only need transaction signatures.
2. Set `rewards: false` if reward data is not needed.
3. Consider using `getBlockHeight` first if you need the latest block.
4. For real-time updates, consider using websocket subscription instead.
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32004 | Block not available for slot | The block was pruned or skipped |
| -32602 | Invalid param: WrongSize | Verify the slot number is valid |
| -32602 | Invalid param: Too large | Request a more recent block |
| -32009 | Transaction version unsupported | Specify `maxSupportedTransactionVersion` |
# getBlockCommitment
Source: https://docs.orbitflare.com/rpc/methods/getBlockCommitment
Returns commitment for particular block
## Parameters
Block number, identified by Slot
## Response
Array of u64 integers logging the amount of cluster stake in lamports that has voted on the block at each depth from 0 to `MAX_LOCKOUT_HISTORY`.
Total active stake, in lamports, of the current epoch.
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockCommitment",
"params": [5]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const commitment = await connection.getBlockCommitment(5);
console.log(commitment);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
response = client.get_block_commitment(5)
print(response)
```
## Notes
1. Returns commitment information for a specific block
2. The commitment array indicates stake weight distribution
3. The totalStake value represents the total active stake in the current epoch
4. The response is immediate as it reads from the current state
## Best Practices
1. Use this method to verify block confirmation status
2. Compare commitment values to understand consensus levels
3. Handle network errors and retry when appropriate
# getBlockHeight
Source: https://docs.orbitflare.com/rpc/methods/getBlockHeight
Returns the current block height of the node
## Parameters
Configuration object containing the following optional fields:
The level of commitment to use:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
The minimum slot that the request can be evaluated at
## Response
Current block height
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockHeight"
}'
```
### Request with Commitment
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockHeight",
"params": [
{
"commitment": "finalized"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const blockHeight = await connection.getBlockHeight('finalized');
console.log(`Current block height: ${blockHeight}`);
```
## Notes
1. Block height increases monotonically with each new block
2. Block height may be different from slot number due to skipped slots
3. Different nodes may return slightly different heights based on their view of the network
4. The response is immediate as it reads from the current state
## Best Practices
1. Use appropriate commitment level based on your needs:
* `processed` for latest possible height
* `confirmed` for high probability finality
* `finalized` for guaranteed finality
2. Consider using `getSlot` if you need slot numbers instead of block height
3. For historical queries, use `getBlock` with specific slots
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Block height not available | Node may be bootstrapping or syncing |
# getBlockProduction
Source: https://docs.orbitflare.com/rpc/methods/getBlockProduction
Returns recent block production information from the current or previous epoch
## Parameters
Configuration object containing the following optional fields:
The level of commitment to use:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
Slot range to return block production for:
* `firstSlot`: number - First slot to return block production information for (inclusive)
* `lastSlot`: number - Last slot to return block production information for (inclusive)
Only return results for this validator identity (base-58 encoded)
## Response
The slot the request was processed at
Dictionary of validator identities, where each contains:
* `leader_slots`: number - Slots assigned as leader
* `blocks_produced`: number - Blocks produced in this epoch
First slot of the block production information (inclusive)
Last slot of block production information (inclusive)
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockProduction"
}'
```
### Request with Range and Identity
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockProduction",
"params": [
{
"range": {
"firstSlot": 100000000,
"lastSlot": 100000100
},
"identity": "GH7ome3EiwEr17v3Fn6XY4RjS1YERxYzwXSW8kZ8kBYq"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const blockProduction = await connection.getBlockProduction();
console.log('Block production:', blockProduction);
// Get production for specific range and validator
const config = {
range: {
firstSlot: 100000000,
lastSlot: 100000100
},
identity: 'GH7ome3EiwEr17v3Fn6XY4RjS1YERxYzwXSW8kZ8kBYq'
};
const specificProduction = await connection.getBlockProduction(config);
```
## Notes
1. Without parameters, returns production for all validators in the current epoch
2. The `range` parameter can span multiple epochs
3. Block production statistics are used to track validator performance
4. Results include both assigned leader slots and actual blocks produced
## Best Practices
1. Use `range` to limit data when querying historical information
2. Use `identity` to track specific validator performance
3. Compare `leader_slots` to `blocks_produced` to evaluate validator reliability
4. Cache results when appropriate to reduce RPC load
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid param: WrongSize | Verify validator identity is valid |
| -32602 | Invalid param: not base58 encoded string | Ensure validator identity is base58 encoded |
| -32602 | Invalid param: slot range too large | Reduce the slot range size |
| -32007 | Block production not available | Node may be bootstrapping or range is too old |
# getBlockTime
Source: https://docs.orbitflare.com/rpc/methods/getBlockTime
Returns the estimated production time of a block
## Parameters
The slot to get the block time for
## Response
Unix timestamp of when the block was produced, or null if the block is not available
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlockTime",
"params": [
100000000
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get block time
const blockTime = await connection.getBlockTime(100000000);
if (blockTime !== null) {
console.log('Block time:', new Date(blockTime * 1000));
} else {
console.log('Block not available');
}
// Get multiple block times
async function getBlockTimes(slots: number[]) {
const times = await Promise.all(
slots.map(slot => connection.getBlockTime(slot))
);
return times.map((time, index) => ({
slot: slots[index],
time: time ? new Date(time * 1000) : null
}));
}
```
## Notes
1. Returns the Unix timestamp when the block was produced
2. Returns null if the block is not available
3. The timestamp is in seconds since Unix epoch
4. Block times are estimates and may vary between nodes
5. Older blocks may not have timestamps available
## Best Practices
1. Handle null responses appropriately
2. Convert Unix timestamps to Date objects for display
3. Consider timezone differences when displaying times
4. Cache results when appropriate to reduce RPC load
5. Use this method in conjunction with other block methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Block time not available | Block may be too old or not yet produced |
## Use Cases
1. **Block Time Analysis**
```typescript theme={null}
interface BlockTimeMetrics {
slot: number;
time: Date | null;
delay: number | null;
}
async function analyzeBlockTimes(startSlot: number, endSlot: number) {
const blocks = await connection.getBlocks(startSlot, endSlot);
const times = await Promise.all(
blocks.map(slot => connection.getBlockTime(slot))
);
const metrics: BlockTimeMetrics[] = blocks.map((slot, index) => {
const time = times[index];
const prevTime = index > 0 ? times[index - 1] : null;
return {
slot,
time: time ? new Date(time * 1000) : null,
delay: time && prevTime ? time - prevTime : null
};
});
return metrics;
}
```
2. **Block Time Tracking**
```typescript theme={null}
interface BlockTimeHistory {
slot: number;
timestamp: number;
date: Date;
}
async function trackBlockTimes(slots: number[]) {
const times = await Promise.all(
slots.map(slot => connection.getBlockTime(slot))
);
const history: BlockTimeHistory[] = times
.filter((time, index) => time !== null)
.map((time, index) => ({
slot: slots[index],
timestamp: time!,
date: new Date(time! * 1000)
}));
return history;
}
```
3. **Block Production Rate**
```typescript theme={null}
async function calculateBlockProductionRate(startSlot: number, endSlot: number) {
const blocks = await connection.getBlocks(startSlot, endSlot);
const times = await Promise.all(
blocks.map(slot => connection.getBlockTime(slot))
);
const validTimes = times.filter(time => time !== null) as number[];
if (validTimes.length < 2) {
return null;
}
const firstTime = validTimes[0];
const lastTime = validTimes[validTimes.length - 1];
const totalTime = lastTime - firstTime;
const blocksPerSecond = validTimes.length / totalTime;
return {
blocksPerSecond,
totalTime,
totalBlocks: validTimes.length
};
}
```
# getBlocks
Source: https://docs.orbitflare.com/rpc/methods/getBlocks
Returns a list of confirmed blocks between two slots
## Parameters
Start slot (inclusive)
End slot (inclusive)
Configuration object containing the following optional fields:
The level of commitment to use:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
## Response
Array of block slots in ascending order
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlocks",
"params": [
100000000,
100000100
]
}'
```
### Request with Commitment
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlocks",
"params": [
100000000,
100000100,
{
"commitment": "finalized"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get blocks in range
const blocks = await connection.getBlocks(100000000, 100000100);
console.log('Blocks:', blocks);
// Get finalized blocks
const finalizedBlocks = await connection.getBlocks(100000000, 100000100, 'finalized');
console.log('Finalized blocks:', finalizedBlocks);
```
## Notes
1. Returns confirmed blocks between the specified slots
2. Results are returned in ascending order
3. The range is inclusive of both start and end slots
4. Some slots may be skipped (no block produced)
5. The response is immediate as it reads from the current state
## Best Practices
1. Use appropriate commitment level based on your needs:
* `processed` for latest blocks
* `confirmed` for high probability finality
* `finalized` for guaranteed finality
2. Keep the slot range reasonable to avoid timeouts
3. Consider using `getBlocksWithLimit` for pagination
4. Cache results when appropriate to reduce RPC load
5. Handle skipped slots in your application logic
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid param: startSlot must be less than endSlot | Ensure startSlot is less than endSlot |
| -32602 | Invalid param: slot range too large | Reduce the slot range size |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Block information unavailable | Node may be bootstrapping or range is too old |
## Use Cases
1. **Block Range Analysis**
```typescript theme={null}
async function analyzeBlockRange(startSlot: number, endSlot: number) {
const blocks = await connection.getBlocks(startSlot, endSlot);
const totalSlots = endSlot - startSlot + 1;
const skippedSlots = totalSlots - blocks.length;
console.log(`Total slots: ${totalSlots}`);
console.log(`Blocks produced: ${blocks.length}`);
console.log(`Skipped slots: ${skippedSlots}`);
console.log(`Block production rate: ${(blocks.length / totalSlots) * 100}%`);
}
```
2. **Block History Tracking**
```typescript theme={null}
interface BlockHistory {
startSlot: number;
endSlot: number;
blocks: number[];
timestamp: Date;
}
async function trackBlockHistory(startSlot: number, endSlot: number) {
const blocks = await connection.getBlocks(startSlot, endSlot);
const history: BlockHistory = {
startSlot,
endSlot,
blocks,
timestamp: new Date()
};
storeBlockHistory(history);
return history;
}
```
3. **Block Gap Detection**
```typescript theme={null}
async function detectBlockGaps(startSlot: number, endSlot: number) {
const blocks = await connection.getBlocks(startSlot, endSlot);
const gaps = [];
for (let i = 0; i < blocks.length - 1; i++) {
const current = blocks[i];
const next = blocks[i + 1];
if (next - current > 1) {
gaps.push({
start: current + 1,
end: next - 1,
length: next - current - 1
});
}
}
return gaps;
}
```
# getBlocksWithLimit
Source: https://docs.orbitflare.com/rpc/methods/getBlocksWithLimit
Returns a list of confirmed blocks starting at the given slot
## Parameters
Start slot (inclusive)
Maximum number of blocks to return
Configuration object containing the following optional fields:
The level of commitment to use:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
## Response
Array of block slots in ascending order
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlocksWithLimit",
"params": [
100000000,
10
]
}'
```
### Request with Commitment
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlocksWithLimit",
"params": [
100000000,
10,
{
"commitment": "finalized"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get 10 blocks starting from slot
const blocks = await connection.getBlocksWithLimit(100000000, 10);
console.log('Blocks:', blocks);
// Get finalized blocks
const finalizedBlocks = await connection.getBlocksWithLimit(100000000, 10, 'finalized');
console.log('Finalized blocks:', finalizedBlocks);
```
## Notes
1. Returns confirmed blocks starting from the specified slot
2. Results are returned in ascending order
3. The limit controls the maximum number of blocks to return
4. Some slots may be skipped (no block produced)
5. The response is immediate as it reads from the current state
## Best Practices
1. Use appropriate commitment level based on your needs:
* `processed` for latest blocks
* `confirmed` for high probability finality
* `finalized` for guaranteed finality
2. Keep the limit reasonable to avoid timeouts
3. Use this method for pagination instead of `getBlocks`
4. Cache results when appropriate to reduce RPC load
5. Handle skipped slots in your application logic
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid param: limit must be positive | Ensure limit is greater than 0 |
| -32602 | Invalid param: limit too large | Reduce the limit size |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Block information unavailable | Node may be bootstrapping or slot is too old |
## Use Cases
1. **Block Pagination**
```typescript theme={null}
async function getBlocksPaginated(startSlot: number, pageSize: number) {
const blocks = await connection.getBlocksWithLimit(startSlot, pageSize);
const lastBlock = blocks[blocks.length - 1];
return {
blocks,
nextStartSlot: lastBlock ? lastBlock + 1 : null
};
}
```
2. **Recent Block History**
```typescript theme={null}
async function getRecentBlocks(count: number) {
const currentSlot = await connection.getSlot();
const blocks = await connection.getBlocksWithLimit(currentSlot - count, count);
return blocks.map(slot => ({
slot,
timestamp: new Date() // Add actual timestamp if available
}));
}
```
3. **Block Streaming**
```typescript theme={null}
async function* streamBlocks(startSlot: number, batchSize: number) {
let currentSlot = startSlot;
while (true) {
const blocks = await connection.getBlocksWithLimit(currentSlot, batchSize);
if (blocks.length === 0) {
break;
}
yield blocks;
currentSlot = blocks[blocks.length - 1] + 1;
}
}
```
# getClusterNodes
Source: https://docs.orbitflare.com/rpc/methods/getClusterNodes
Returns information about all the nodes participating in the cluster
## Parameters
This method does not take any parameters.
## Response
Array of objects containing node information:
Node public key (base-58)
Gossip network address for the node
TPU network address for the node
JSON RPC network address for the node
Software version of the node
Unique identifier of the node's feature set
The shred version the node has been configured to use
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getClusterNodes"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const nodes = await connection.getClusterNodes();
console.log('Cluster nodes:', nodes);
// Example: Find all nodes running a specific version
const version = '1.16.0';
const nodesWithVersion = nodes.filter(node => node.version === version);
console.log(`Nodes running version ${version}:`, nodesWithVersion);
// Example: Get all RPC endpoints
const rpcEndpoints = nodes
.filter(node => node.rpc)
.map(node => node.rpc);
console.log('Available RPC endpoints:', rpcEndpoints);
```
## Notes
1. Some fields may be `null` if the information is not available
2. Node information is obtained through the gossip network
3. The list includes all known nodes, whether they are currently active or not
4. Network addresses are in standard socket format (IP:port)
## Best Practices
1. Cache results to reduce RPC load (refresh every few minutes)
2. Use version information to detect network upgrades
3. Filter null values when processing network addresses
4. Consider node versions when selecting RPC endpoints
5. Use TPU addresses for transaction forwarding optimization
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Node information unavailable | Node may be bootstrapping or gossip service may be down |
| -32008 | Node list too large | Try again later when network conditions improve |
## Use Cases
1. **Load Balancing**
* Discover available RPC endpoints
* Distribute client connections
2. **Network Health Monitoring**
* Track node versions
* Monitor network distribution
* Identify network partitions
3. **Transaction Optimization**
* Find closest TPU addresses
* Implement leader-aware transaction forwarding
4. **Version Management**
* Track network upgrades
* Ensure client compatibility
* Plan maintenance windows
# getEpochInfo
Source: https://docs.orbitflare.com/rpc/methods/getEpochInfo
Returns information about the current epoch
## Parameters
The commitment describes how finalized a block is at that point in time.
The minimum slot that the request can be evaluated at.
## Response
The current slot
The current block height
The current epoch
The current slot relative to the start of the current epoch
The number of slots in this epoch
Total number of transactions processed without error since genesis
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getEpochInfo"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const epochInfo = await connection.getEpochInfo();
console.log(epochInfo);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
response = client.get_epoch_info()
print(response)
```
## Notes
1. Returns detailed information about the current epoch
2. Includes absolute slot, block height, epoch number, and slots in epoch
3. Transaction count may be null if not available
4. Different commitment levels can be specified
5. The response is immediate as it reads from the current state
## Best Practices
1. Use this method to track epoch transitions
2. Monitor transaction count for network activity
3. Check slot index to determine progress within the current epoch
4. Handle network errors and retry when appropriate
5. Use appropriate commitment level based on your needs
# getEpochSchedule
Source: https://docs.orbitflare.com/rpc/methods/getEpochSchedule
Returns epoch schedule information from this cluster genesis config
## Parameters
This method does not require any parameters.
## Response
The maximum number of slots in each epoch
The number of slots before beginning of an epoch to calculate a leader schedule for that epoch
Whether epochs start short and grow
First normal-length epoch, log2(slotsPerEpoch) - log2(MINIMUM\_SLOTS\_PER\_EPOCH)
MINIMUM\_SLOTS\_PER\_EPOCH \* (2.pow(firstNormalEpoch) - 1)
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getEpochSchedule"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const epochSchedule = await connection.getEpochSchedule();
console.log(epochSchedule);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
response = client.get_epoch_schedule()
print(response)
```
## Notes
1. Returns epoch schedule information from the cluster's genesis configuration
2. The returned data is static and does not change for a given network/cluster
3. Useful for calculating epoch boundaries and leader schedule timing
4. The warmup parameter indicates if epochs start short and grow to their full size
## Best Practices
1. Cache this information locally as it does not change
2. Use the returned values to calculate epoch transitions
3. Use firstNormalEpoch and firstNormalSlot for epoch timing calculations
4. Handle network errors and retry when appropriate
# getFeeForMessage
Source: https://docs.orbitflare.com/rpc/methods/getFeeForMessage
Get the fee the network will charge for a particular message
## Parameters
Base-64 encoded Message
The commitment level to use for the fee calculation
## Response
The slot this request was processed in
Fee in lamports that will be charged for processing the message, or null if the message is invalid or the fee cannot be calculated
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getFeeForMessage",
"params": [
"AQABAyRn/PA8jzJtN6oAyB3VR0nfOF0xnfkYaKwY4Ir3nrHFJGqhAQICAAEDAgABDAIAAAAEAAAAAAAA",
{"commitment": "processed"}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const message = 'AQABAyRn/PA8jzJtN6oAyB3VR0nfOF0xnfkYaKwY4Ir3nrHFJGqhAQICAAEDAgABDAIAAAAEAAAAAAAA';
const fee = await connection.getFeeForMessage(message);
console.log(fee);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
message = "AQABAyRn/PA8jzJtN6oAyB3VR0nfOF0xnfkYaKwY4Ir3nrHFJGqhAQICAAEDAgABDAIAAAAEAAAAAAAA"
response = client.get_fee_for_message(message)
print(response)
```
## Notes
1. Returns the fee the network will charge for a particular message
2. Useful for calculating transaction fees more accurately
3. Preferred over older fee calculation methods
4. Different commitment levels can be specified
5. Returns null if the message is invalid or can't be processed
## Best Practices
1. Use this method for accurate fee prediction before sending transactions
2. Provide a valid base64-encoded message to avoid null responses
3. Handle null responses appropriately in your application
4. Handle network errors and retry when appropriate
5. Use this method when precise fee calculation is needed
# getFirstAvailableBlock
Source: https://docs.orbitflare.com/rpc/methods/getFirstAvailableBlock
Returns the slot of the lowest confirmed block that the node has information about
## Parameters
This method does not take any parameters.
## Response
The slot of the lowest confirmed block available
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getFirstAvailableBlock"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get first available block
const firstBlock = await connection.getFirstAvailableBlock();
console.log('First available block:', firstBlock);
// Get block range
async function getAvailableBlockRange() {
const firstBlock = await connection.getFirstAvailableBlock();
const currentSlot = await connection.getSlot();
return {
firstBlock,
currentSlot,
totalBlocks: currentSlot - firstBlock + 1
};
}
```
## Notes
1. Returns the slot of the lowest confirmed block available
2. This is useful for determining the node's available history
3. The value may change as the node prunes old blocks
4. Different nodes may return different values
5. The response is immediate as it reads from the current state
## Best Practices
1. Use this method to determine available block range
2. Consider node pruning when querying historical blocks
3. Cache results when appropriate to reduce RPC load
4. Handle cases where requested blocks are not available
5. Use in conjunction with other block methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Block information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Block Range Verification**
```typescript theme={null}
async function verifyBlockRange(startSlot: number, endSlot: number) {
const firstBlock = await connection.getFirstAvailableBlock();
const currentSlot = await connection.getSlot();
if (startSlot < firstBlock) {
throw new Error(`Start slot ${startSlot} is before first available block ${firstBlock}`);
}
if (endSlot > currentSlot) {
throw new Error(`End slot ${endSlot} is after current slot ${currentSlot}`);
}
return {
startSlot,
endSlot,
available: true
};
}
```
2. **Historical Data Availability**
```typescript theme={null}
interface BlockAvailability {
firstAvailable: number;
currentSlot: number;
availableRange: number;
requestedSlot: number;
isAvailable: boolean;
}
async function checkBlockAvailability(slot: number): Promise {
const firstBlock = await connection.getFirstAvailableBlock();
const currentSlot = await connection.getSlot();
return {
firstAvailable: firstBlock,
currentSlot,
availableRange: currentSlot - firstBlock + 1,
requestedSlot: slot,
isAvailable: slot >= firstBlock && slot <= currentSlot
};
}
```
3. **Block History Analysis**
```typescript theme={null}
async function analyzeBlockHistory() {
const firstBlock = await connection.getFirstAvailableBlock();
const currentSlot = await connection.getSlot();
const blocks = await connection.getBlocks(firstBlock, currentSlot);
const metrics = {
firstBlock,
currentSlot,
totalSlots: currentSlot - firstBlock + 1,
availableBlocks: blocks.length,
missingBlocks: (currentSlot - firstBlock + 1) - blocks.length,
availabilityPercentage: (blocks.length / (currentSlot - firstBlock + 1)) * 100
};
return metrics;
}
```
# getGenesisHash
Source: https://docs.orbitflare.com/rpc/methods/getGenesisHash
Returns the genesis hash of the cluster
## Parameters
This method does not take any parameters.
## Response
The genesis hash as a base-58 encoded string
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getGenesisHash"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get genesis hash
const genesisHash = await connection.getGenesisHash();
console.log('Genesis hash:', genesisHash);
// Verify cluster
async function verifyCluster(expectedHash: string) {
const actualHash = await connection.getGenesisHash();
if (actualHash !== expectedHash) {
throw new Error(`Connected to wrong cluster. Expected ${expectedHash}, got ${actualHash}`);
}
console.log('Connected to correct cluster');
}
```
## Notes
1. Returns the genesis hash of the cluster
2. The genesis hash is unique to each cluster
3. This can be used to verify which cluster you're connected to
4. The hash is base-58 encoded
5. The response is immediate as it reads from the current state
## Best Practices
1. Use this method to verify cluster connection
2. Cache the result to reduce RPC load
3. Compare against known genesis hashes for different clusters
4. Use in conjunction with other cluster verification methods
5. Handle network errors appropriately
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Genesis hash unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Cluster Verification**
```typescript theme={null}
const CLUSTER_HASHES = {
mainnet: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d',
testnet: '4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY',
devnet: 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG'
};
async function verifyClusterConnection(expectedCluster: keyof typeof CLUSTER_HASHES) {
const genesisHash = await connection.getGenesisHash();
const expectedHash = CLUSTER_HASHES[expectedCluster];
if (genesisHash !== expectedHash) {
throw new Error(`Connected to wrong cluster. Expected ${expectedCluster} (${expectedHash}), got ${genesisHash}`);
}
return {
cluster: expectedCluster,
genesisHash,
verified: true
};
}
```
2. **Cluster Detection**
```typescript theme={null}
async function detectCluster() {
const genesisHash = await connection.getGenesisHash();
for (const [cluster, hash] of Object.entries(CLUSTER_HASHES)) {
if (hash === genesisHash) {
return cluster;
}
}
throw new Error('Unknown cluster');
}
```
3. **Network Configuration**
```typescript theme={null}
interface NetworkConfig {
cluster: string;
genesisHash: string;
rpcEndpoint: string;
wsEndpoint: string;
}
async function configureNetwork(endpoint: string): Promise {
const connection = new Connection(endpoint);
const genesisHash = await connection.getGenesisHash();
const cluster = await detectCluster();
return {
cluster,
genesisHash,
rpcEndpoint: endpoint,
wsEndpoint: endpoint.replace('http', 'ws')
};
}
```
# getHealth
Source: https://docs.orbitflare.com/rpc/methods/getHealth
Returns the current health status of the node
## Parameters
This method does not take any parameters.
## Response
One of the following status values:
* `"ok"`: Node is healthy and up-to-date
* `"behind"`: Node is behind by some number of slots
* `"unknown"`: Node health cannot be determined
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getHealth"
}'
```
### Alternative HTTP GET Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY/health
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const health = await connection.getHealth();
console.log('Node health:', health);
// Example: Check if node is healthy
const isHealthy = health === 'ok';
if (!isHealthy) {
console.warn('Node is not healthy:', health);
}
```
## Notes
1. This method is commonly used for load balancer health checks
2. The HTTP GET endpoint `/health` provides the same information
3. A node is considered "behind" if it's more than `HEALTH_CHECK_SLOT_DISTANCE` slots from the latest cluster slot
4. The "unknown" status typically indicates the node is bootstrapping or having issues
## Best Practices
1. Use this endpoint for basic health monitoring
2. Implement circuit breakers based on health status
3. Consider using more detailed methods for specific health metrics:
* `getVersion` for software version
* `getSlot` for slot progress
* `getBlockHeight` for block height
4. Set appropriate timeouts for health checks
5. Handle all possible response values
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32603 | Internal error | Node may be experiencing issues |
| 503 | Service Unavailable | Node is not ready to handle requests |
## Use Cases
1. **Load Balancer Configuration**
```nginx theme={null}
location /health {
proxy_pass http://backend;
proxy_next_upstream error timeout http_503;
}
```
2. **Health Monitoring**
```typescript theme={null}
async function monitorHealth(endpoint: string) {
const connection = new Connection(endpoint);
try {
const health = await connection.getHealth();
if (health !== 'ok') {
alertNodeUnhealthy(endpoint, health);
}
} catch (error) {
alertNodeError(endpoint, error);
}
}
```
3. **Client-Side Load Balancing**
```typescript theme={null}
async function getHealthyEndpoint(endpoints: string[]) {
for (const endpoint of endpoints) {
const connection = new Connection(endpoint);
try {
const health = await connection.getHealth();
if (health === 'ok') {
return endpoint;
}
} catch (error) {
continue;
}
}
throw new Error('No healthy endpoints available');
}
```
4. **System Status Dashboard**
```typescript theme={null}
interface NodeStatus {
endpoint: string;
health: string;
lastChecked: Date;
}
async function updateDashboard(nodes: NodeStatus[]) {
for (const node of nodes) {
const connection = new Connection(node.endpoint);
try {
node.health = await connection.getHealth();
} catch (error) {
node.health = 'error';
}
node.lastChecked = new Date();
}
renderDashboard(nodes);
}
```
# getHighestSnapshotSlot
Source: https://docs.orbitflare.com/rpc/methods/getHighestSnapshotSlot
Returns the highest slot information from a snapshot
## Parameters
This method does not take any parameters.
## Response
Highest full snapshot slot
Highest incremental snapshot slot, or null if no incremental snapshots are available
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getHighestSnapshotSlot"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get highest snapshot slot
const snapshotSlot = await connection.getHighestSnapshotSlot();
console.log('Highest full snapshot slot:', snapshotSlot.full);
if (snapshotSlot.incremental !== null) {
console.log('Highest incremental snapshot slot:', snapshotSlot.incremental);
}
// Check snapshot availability
async function checkSnapshotAvailability() {
const snapshotSlot = await connection.getHighestSnapshotSlot();
const currentSlot = await connection.getSlot();
return {
fullSnapshotAvailable: snapshotSlot.full > 0,
incrementalSnapshotAvailable: snapshotSlot.incremental !== null,
fullSnapshotSlot: snapshotSlot.full,
incrementalSnapshotSlot: snapshotSlot.incremental,
currentSlot,
fullSnapshotAge: currentSlot - snapshotSlot.full,
incrementalSnapshotAge: snapshotSlot.incremental ? currentSlot - snapshotSlot.incremental : null
};
}
```
## Notes
1. Returns information about the highest snapshot slots
2. Full snapshots contain the complete ledger state
3. Incremental snapshots contain changes since the last full snapshot
4. Incremental snapshots may not be available on all nodes
5. The response is immediate as it reads from the current state
## Best Practices
1. Use this method to determine snapshot availability
2. Consider snapshot age when planning operations
3. Cache results when appropriate to reduce RPC load
4. Handle cases where incremental snapshots are not available
5. Use in conjunction with other snapshot methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Snapshot information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Snapshot Analysis**
```typescript theme={null}
interface SnapshotMetrics {
fullSnapshot: {
slot: number;
age: number;
available: boolean;
};
incrementalSnapshot: {
slot: number | null;
age: number | null;
available: boolean;
};
currentSlot: number;
}
async function analyzeSnapshots(): Promise {
const snapshotSlot = await connection.getHighestSnapshotSlot();
const currentSlot = await connection.getSlot();
return {
fullSnapshot: {
slot: snapshotSlot.full,
age: currentSlot - snapshotSlot.full,
available: snapshotSlot.full > 0
},
incrementalSnapshot: {
slot: snapshotSlot.incremental,
age: snapshotSlot.incremental ? currentSlot - snapshotSlot.incremental : null,
available: snapshotSlot.incremental !== null
},
currentSlot
};
}
```
2. **Snapshot Monitoring**
```typescript theme={null}
interface SnapshotAlert {
type: 'full' | 'incremental';
message: string;
currentSlot: number;
snapshotSlot: number;
age: number;
}
async function monitorSnapshots(maxAge: number): Promise {
const snapshotSlot = await connection.getHighestSnapshotSlot();
const currentSlot = await connection.getSlot();
const alerts: SnapshotAlert[] = [];
if (snapshotSlot.full > 0) {
const fullAge = currentSlot - snapshotSlot.full;
if (fullAge > maxAge) {
alerts.push({
type: 'full',
message: `Full snapshot is ${fullAge} slots old`,
currentSlot,
snapshotSlot: snapshotSlot.full,
age: fullAge
});
}
}
if (snapshotSlot.incremental !== null) {
const incrementalAge = currentSlot - snapshotSlot.incremental;
if (incrementalAge > maxAge) {
alerts.push({
type: 'incremental',
message: `Incremental snapshot is ${incrementalAge} slots old`,
currentSlot,
snapshotSlot: snapshotSlot.incremental,
age: incrementalAge
});
}
}
return alerts;
}
```
3. **Snapshot Verification**
```typescript theme={null}
async function verifySnapshotAvailability() {
const snapshotSlot = await connection.getHighestSnapshotSlot();
const currentSlot = await connection.getSlot();
if (snapshotSlot.full === 0) {
throw new Error('No full snapshots available');
}
const fullAge = currentSlot - snapshotSlot.full;
if (fullAge > 100000) { // Alert if snapshot is more than 100k slots old
console.warn(`Full snapshot is ${fullAge} slots old`);
}
if (snapshotSlot.incremental === null) {
console.warn('No incremental snapshots available');
} else {
const incrementalAge = currentSlot - snapshotSlot.incremental;
if (incrementalAge > 10000) { // Alert if incremental snapshot is more than 10k slots old
console.warn(`Incremental snapshot is ${incrementalAge} slots old`);
}
}
return {
fullSnapshot: snapshotSlot.full,
incrementalSnapshot: snapshotSlot.incremental,
currentSlot,
fullAge,
incrementalAge: snapshotSlot.incremental ? currentSlot - snapshotSlot.incremental : null
};
}
```
# getIdentity
Source: https://docs.orbitflare.com/rpc/methods/getIdentity
Returns the identity pubkey for the current node
## Parameters
This method does not require any parameters.
## Response
The identity pubkey of the current node (base-58 encoded)
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getIdentity"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const identity = await connection.getIdentity();
console.log(identity);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
response = client.get_identity()
print(response)
```
## Notes
1. Returns the identity public key of the validator node you're connected to
2. Useful for identifying which node you're communicating with
3. The response is immediate as it reads from the node's configuration
4. Can be used to verify you're connected to the expected node
## Best Practices
1. Use this method when troubleshooting node connectivity issues
2. Compare returned identity with expected node identity for validation
3. Handle network errors and retry when appropriate
# getInflationGovernor
Source: https://docs.orbitflare.com/rpc/methods/getInflationGovernor
Returns the current inflation governor parameters
## Parameters
This method does not take any parameters.
## Response
Initial inflation percentage from time 0
Terminal inflation percentage
Rate per year at which inflation is lowered
Percentage of total inflation allocated to the foundation
Duration of foundation pool inflation in years
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getInflationGovernor"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get inflation governor parameters
const inflationGovernor = await connection.getInflationGovernor();
console.log('Initial inflation:', inflationGovernor.initial);
console.log('Terminal inflation:', inflationGovernor.terminal);
console.log('Taper rate:', inflationGovernor.taper);
console.log('Foundation percentage:', inflationGovernor.foundation);
console.log('Foundation term:', inflationGovernor.foundationTerm);
// Calculate current inflation rate
async function calculateCurrentInflation() {
const inflationGovernor = await connection.getInflationGovernor();
const epochInfo = await connection.getEpochInfo();
const yearsSinceGenesis = epochInfo.absoluteSlot / (432000 * 365); // Approximate years
const currentInflation = Math.max(
inflationGovernor.terminal,
inflationGovernor.initial - (inflationGovernor.taper * yearsSinceGenesis)
);
return {
currentInflation,
yearsSinceGenesis,
initialInflation: inflationGovernor.initial,
terminalInflation: inflationGovernor.terminal,
taperRate: inflationGovernor.taper
};
}
```
## Notes
1. Returns the parameters that govern inflation on the Solana network
2. Inflation decreases over time according to the taper rate
3. Foundation inflation is separate from validator inflation
4. The response is immediate as it reads from the current state
5. These parameters are set by governance and can change
## Best Practices
1. Use this method to understand the network's inflation schedule
2. Cache results when appropriate to reduce RPC load
3. Consider the impact of inflation on staking rewards
4. Monitor for changes in inflation parameters
5. Use in conjunction with other inflation-related methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Inflation information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Inflation Analysis**
```typescript theme={null}
interface InflationMetrics {
currentInflation: number;
yearsSinceGenesis: number;
initialInflation: number;
terminalInflation: number;
taperRate: number;
foundationPercentage: number;
foundationTerm: number;
validatorInflation: number;
foundationInflation: number;
}
async function analyzeInflation(): Promise {
const inflationGovernor = await connection.getInflationGovernor();
const epochInfo = await connection.getEpochInfo();
const yearsSinceGenesis = epochInfo.absoluteSlot / (432000 * 365);
const currentInflation = Math.max(
inflationGovernor.terminal,
inflationGovernor.initial - (inflationGovernor.taper * yearsSinceGenesis)
);
const foundationInflation = yearsSinceGenesis < inflationGovernor.foundationTerm
? currentInflation * inflationGovernor.foundation
: 0;
return {
currentInflation,
yearsSinceGenesis,
initialInflation: inflationGovernor.initial,
terminalInflation: inflationGovernor.terminal,
taperRate: inflationGovernor.taper,
foundationPercentage: inflationGovernor.foundation,
foundationTerm: inflationGovernor.foundationTerm,
validatorInflation: currentInflation - foundationInflation,
foundationInflation
};
}
```
2. **Staking Reward Projection**
```typescript theme={null}
interface StakingProjection {
currentAPY: number;
futureAPY: number[];
years: number[];
}
async function projectStakingRewards(
totalStaked: number,
totalSupply: number,
years: number = 10
): Promise {
const inflationGovernor = await connection.getInflationGovernor();
const epochInfo = await connection.getEpochInfo();
const currentYear = epochInfo.absoluteSlot / (432000 * 365);
const yearsArray = Array.from({ length: years }, (_, i) => i);
const futureAPY = yearsArray.map(year => {
const inflation = Math.max(
inflationGovernor.terminal,
inflationGovernor.initial - (inflationGovernor.taper * (currentYear + year))
);
const foundationInflation = (currentYear + year) < inflationGovernor.foundationTerm
? inflation * inflationGovernor.foundation
: 0;
const validatorInflation = inflation - foundationInflation;
return (validatorInflation * totalSupply) / totalStaked;
});
return {
currentAPY: futureAPY[0],
futureAPY,
years: yearsArray
};
}
```
3. **Inflation Monitoring**
```typescript theme={null}
interface InflationAlert {
type: 'high' | 'low' | 'change';
message: string;
currentInflation: number;
expectedInflation: number;
difference: number;
}
async function monitorInflation(
threshold: number = 0.01
): Promise {
const inflationGovernor = await connection.getInflationGovernor();
const epochInfo = await connection.getEpochInfo();
const yearsSinceGenesis = epochInfo.absoluteSlot / (432000 * 365);
const expectedInflation = Math.max(
inflationGovernor.terminal,
inflationGovernor.initial - (inflationGovernor.taper * yearsSinceGenesis)
);
const currentInflation = await connection.getInflationRate();
const difference = Math.abs(currentInflation - expectedInflation);
const alerts: InflationAlert[] = [];
if (difference > threshold) {
alerts.push({
type: 'change',
message: `Inflation differs from expected by ${difference.toFixed(4)}`,
currentInflation,
expectedInflation,
difference
});
}
if (currentInflation > inflationGovernor.initial) {
alerts.push({
type: 'high',
message: `Inflation is above initial rate`,
currentInflation,
expectedInflation,
difference
});
}
if (currentInflation < inflationGovernor.terminal) {
alerts.push({
type: 'low',
message: `Inflation is below terminal rate`,
currentInflation,
expectedInflation,
difference
});
}
return alerts;
}
```
# getInflationRate
Source: https://docs.orbitflare.com/rpc/methods/getInflationRate
Returns the current inflation rate of the network
## Parameters
This method does not take any parameters.
## Response
Total inflation rate as a percentage
Inflation rate allocated to validators as a percentage
Inflation rate allocated to the foundation as a percentage
Current epoch number
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getInflationRate"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get current inflation rate
const inflationRate = await connection.getInflationRate();
console.log('Total inflation:', inflationRate.total);
console.log('Validator inflation:', inflationRate.validator);
console.log('Foundation inflation:', inflationRate.foundation);
console.log('Current epoch:', inflationRate.epoch);
// Calculate staking APY
async function calculateStakingAPY(totalStaked: number, totalSupply: number) {
const inflationRate = await connection.getInflationRate();
const validatorAPY = (inflationRate.validator * totalSupply) / totalStaked;
return {
validatorAPY,
totalInflation: inflationRate.total,
validatorInflation: inflationRate.validator,
foundationInflation: inflationRate.foundation,
epoch: inflationRate.epoch
};
}
```
## Notes
1. Returns the current inflation rate and its allocation
2. Total inflation is split between validators and foundation
3. Inflation rates are expressed as percentages
4. The response is immediate as it reads from the current state
5. Inflation rates can change between epochs
## Best Practices
1. Use this method to understand current network inflation
2. Cache results when appropriate to reduce RPC load
3. Consider the impact of inflation on staking rewards
4. Monitor for changes in inflation rates
5. Use in conjunction with other inflation-related methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Inflation information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Inflation Tracking**
```typescript theme={null}
interface InflationHistory {
epoch: number;
total: number;
validator: number;
foundation: number;
timestamp: number;
}
async function trackInflationHistory(
epochs: number = 10
): Promise {
const history: InflationHistory[] = [];
const currentEpoch = (await connection.getEpochInfo()).epoch;
for (let i = 0; i < epochs; i++) {
const epoch = currentEpoch - i;
const inflationRate = await connection.getInflationRate();
history.push({
epoch: inflationRate.epoch,
total: inflationRate.total,
validator: inflationRate.validator,
foundation: inflationRate.foundation,
timestamp: Date.now()
});
// Add delay to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 1000));
}
return history;
}
```
2. **Staking Analysis**
```typescript theme={null}
interface StakingMetrics {
currentAPY: number;
projectedAPY: number;
totalStaked: number;
totalSupply: number;
inflationRate: number;
validatorInflation: number;
}
async function analyzeStakingMetrics(): Promise {
const inflationRate = await connection.getInflationRate();
const supply = await connection.getSupply();
const voteAccounts = await connection.getVoteAccounts();
const totalStaked = voteAccounts.current
.concat(voteAccounts.delinquent)
.reduce((sum, account) => sum + account.activatedStake, 0);
const currentAPY = (inflationRate.validator * supply.value.total) / totalStaked;
// Project future APY based on current trends
const projectedAPY = currentAPY * 0.95; // Assuming 5% decrease
return {
currentAPY,
projectedAPY,
totalStaked,
totalSupply: supply.value.total,
inflationRate: inflationRate.total,
validatorInflation: inflationRate.validator
};
}
```
3. **Inflation Alerts**
```typescript theme={null}
interface InflationAlert {
type: 'high' | 'low' | 'change';
message: string;
currentRate: number;
previousRate: number;
difference: number;
}
class InflationMonitor {
private previousRate: number | null = null;
private threshold: number;
constructor(threshold: number = 0.01) {
this.threshold = threshold;
}
async checkInflation(): Promise {
const inflationRate = await connection.getInflationRate();
const alerts: InflationAlert[] = [];
if (this.previousRate !== null) {
const difference = Math.abs(inflationRate.total - this.previousRate);
if (difference > this.threshold) {
alerts.push({
type: 'change',
message: `Inflation rate changed by ${difference.toFixed(4)}`,
currentRate: inflationRate.total,
previousRate: this.previousRate,
difference
});
}
}
// Check for unusually high or low rates
if (inflationRate.total > 0.08) { // 8% threshold
alerts.push({
type: 'high',
message: `Inflation rate is unusually high: ${inflationRate.total}`,
currentRate: inflationRate.total,
previousRate: this.previousRate || inflationRate.total,
difference: 0
});
}
if (inflationRate.total < 0.01) { // 1% threshold
alerts.push({
type: 'low',
message: `Inflation rate is unusually low: ${inflationRate.total}`,
currentRate: inflationRate.total,
previousRate: this.previousRate || inflationRate.total,
difference: 0
});
}
this.previousRate = inflationRate.total;
return alerts;
}
}
```
# getInflationReward
Source: https://docs.orbitflare.com/rpc/methods/getInflationReward
Returns the inflation reward for a list of addresses for an epoch
## Parameters
An array of addresses to query, as base-58 encoded strings
An epoch for which the reward occurs. If omitted, the previous epoch will be used
The minimum slot that the request can be evaluated at
## Response
Epoch for which reward occured
The slot in which the rewards are effective
Reward amount in lamports
Post balance of the account in lamports
Vote account commission when the reward was credited
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getInflationReward",
"params": [
[
"6dmNQ5jwLeLk5REvio1JcMshcbvkYMwy26sJ8pbkvStu",
"BGsqMegLpV6n6Ve146sSX2dTjUMj3M92HnU8BbNRMhF2"
],
{"epoch": 2}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const addresses = [
'6dmNQ5jwLeLk5REvio1JcMshcbvkYMwy26sJ8pbkvStu',
'BGsqMegLpV6n6Ve146sSX2dTjUMj3M92HnU8BbNRMhF2'
];
const rewards = await connection.getInflationReward(addresses, 2);
console.log(rewards);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
addresses = [
"6dmNQ5jwLeLk5REvio1JcMshcbvkYMwy26sJ8pbkvStu",
"BGsqMegLpV6n6Ve146sSX2dTjUMj3M92HnU8BbNRMhF2"
]
response = client.get_inflation_reward(addresses, 2)
print(response)
```
## Notes
1. Returns inflation rewards for a list of addresses
2. Results are returned as an array in the same order as the input addresses
3. A null value in the result array means that the address has not received rewards
4. Commission field is only present for validators (null for regular accounts)
5. Rewards are calculated at the end of each epoch
## Best Practices
1. Provide a list of valid addresses to avoid null responses
2. Specify the epoch parameter if you need historical reward data
3. Handle null values in the response array appropriately
4. Use this method to track rewards for staking accounts
5. Handle network errors and retry when appropriate
# getLargestAccounts
Source: https://docs.orbitflare.com/rpc/methods/getLargestAccounts
Returns the 20 largest accounts, by lamport balance
## Parameters
Configuration object containing the following optional fields:
The level of commitment to use:
* `processed`: Latest block (unconfirmed)
* `confirmed`: Confirmed by supermajority
* `finalized`: Finalized by supermajority
Filter results by account type:
* `circulating`: Filter to circulating accounts
* `nonCirculating`: Filter to non-circulating accounts
## Response
The slot the request was processed at
Array of objects containing account information:
Address of the account (base-58 encoded)
Number of lamports in the account
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getLargestAccounts"
}'
```
### Request with Filter
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getLargestAccounts",
"params": [
{
"filter": "circulating"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get all largest accounts
const largestAccounts = await connection.getLargestAccounts();
console.log('Largest accounts:', largestAccounts);
// Get circulating accounts only
const circulatingAccounts = await connection.getLargestAccounts({
filter: 'circulating'
});
console.log('Largest circulating accounts:', circulatingAccounts);
```
## Notes
1. Returns the top 20 accounts by lamport balance
2. Results are sorted in descending order by balance
3. Circulating accounts are those not owned by the system program
4. Non-circulating accounts are typically owned by the system program
5. The response is immediate as it reads from the current state
## Best Practices
1. Use appropriate commitment level based on your needs:
* `processed` for UI updates
* `confirmed` for most operations
* `finalized` for critical operations
2. Use the `filter` parameter to focus on relevant accounts
3. Convert lamports to SOL by dividing by 1e9 when displaying to users
4. Consider caching results for UI updates to reduce RPC load
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32602 | Invalid param: Invalid filter | Use either 'circulating' or 'nonCirculating' |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Account information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Network Analysis**
```typescript theme={null}
async function analyzeNetworkDistribution() {
const accounts = await connection.getLargestAccounts();
const total = accounts.reduce((sum, acc) => sum + acc.lamports, 0);
const top20Percentage = accounts.reduce((sum, acc) => sum + acc.lamports, 0) / total;
console.log(`Top 20 accounts hold ${top20Percentage * 100}% of total SOL`);
}
```
2. **Token Distribution Monitoring**
```typescript theme={null}
async function monitorTokenDistribution() {
const circulating = await connection.getLargestAccounts({ filter: 'circulating' });
const nonCirculating = await connection.getLargestAccounts({ filter: 'nonCirculating' });
const circulatingTotal = circulating.reduce((sum, acc) => sum + acc.lamports, 0);
const nonCirculatingTotal = nonCirculating.reduce((sum, acc) => sum + acc.lamports, 0);
console.log('Circulating supply:', circulatingTotal / 1e9, 'SOL');
console.log('Non-circulating supply:', nonCirculatingTotal / 1e9, 'SOL');
}
```
3. **Whale Watching**
```typescript theme={null}
interface WhaleAlert {
address: string;
balance: number;
change: number;
}
async function trackWhaleMovements(previousAccounts: any[]) {
const currentAccounts = await connection.getLargestAccounts();
const alerts: WhaleAlert[] = [];
for (const current of currentAccounts) {
const previous = previousAccounts.find(p => p.address === current.address);
if (previous) {
const change = current.lamports - previous.lamports;
if (Math.abs(change) > 1e9) { // Alert on changes > 1 SOL
alerts.push({
address: current.address,
balance: current.lamports / 1e9,
change: change / 1e9
});
}
}
}
return alerts;
}
```
# getLatestBlockhash
Source: https://docs.orbitflare.com/rpc/methods/getLatestBlockhash
Returns the latest blockhash
## Parameters
The commitment level to use for the response
The minimum slot that the request can be evaluated at
## Response
The slot this request was processed in
The latest blockhash (base-58 encoded)
Last valid block height for the blockhash
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getLatestBlockhash",
"params": [
{"commitment": "processed"}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
console.log(blockhash, lastValidBlockHeight);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
response = client.get_latest_blockhash()
print(response)
```
## Notes
1. This method returns the latest blockhash and how long it will be valid for
2. Replaces the deprecated `getRecentBlockhash` method
3. The blockhash is used for transaction processing and deduplication
4. The blockhash is valid until the specified lastValidBlockHeight is reached
5. Different commitment levels can be specified for the response
## Best Practices
1. Use blockhash for transaction submission and signing
2. Monitor lastValidBlockHeight to determine blockhash validity
3. Refresh blockhash if lastValidBlockHeight is reached or exceeded
4. Handle network errors and retry when appropriate
5. Cache the blockhash for multiple transactions within its validity period
# getLeaderSchedule
Source: https://docs.orbitflare.com/rpc/methods/getLeaderSchedule
Returns the leader schedule for an epoch
## Parameters
Fetch the leader schedule for the epoch containing this slot
Commitment level (processed, confirmed, finalized)
Only return results for this validator identity (base-58 encoded)
## Response
Slot at which the request was processed
Dictionary of validator identities to their assigned leader slots
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getLeaderSchedule"
}'
```
### Request for Specific Validator
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getLeaderSchedule",
"params": [{
"identity": "VALIDATOR_IDENTITY"
}]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get leader schedule
const leaderSchedule = await connection.getLeaderSchedule();
console.log('Leader schedule:', leaderSchedule.value);
// Get validator's leader slots
async function getValidatorLeaderSlots(validatorIdentity: string) {
const schedule = await connection.getLeaderSchedule({
identity: validatorIdentity
});
if (!schedule.value) {
return {
hasSlots: false,
slots: []
};
}
const slots = schedule.value[validatorIdentity] || [];
return {
hasSlots: slots.length > 0,
slots,
totalSlots: slots.length,
context: schedule.context
};
}
```
## Notes
1. Returns the leader schedule for the current or specified epoch
2. Can filter to show only a specific validator's slots
3. Leader slots are assigned to validators based on stake weight
4. The schedule is fixed for the duration of an epoch
5. Response includes the slot at which the request was processed
## Best Practices
1. Use appropriate commitment level based on your needs
2. Filter by validator identity when only interested in specific validators
3. Cache results when appropriate to reduce RPC load
4. Monitor for changes in leader schedule
5. Use in conjunction with other validator-related methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check slot and identity parameters |
| -32007 | Schedule information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Validator Analysis**
```typescript theme={null}
interface ValidatorMetrics {
identity: string;
totalSlots: number;
slotDistribution: number[];
averageGap: number;
maxGap: number;
minGap: number;
}
async function analyzeValidatorSlots(
validatorIdentity: string
): Promise {
const schedule = await connection.getLeaderSchedule({
identity: validatorIdentity
});
if (!schedule.value || !schedule.value[validatorIdentity]) {
throw new Error('No leader slots found for validator');
}
const slots = schedule.value[validatorIdentity].sort((a, b) => a - b);
const gaps = [];
for (let i = 1; i < slots.length; i++) {
gaps.push(slots[i] - slots[i - 1]);
}
return {
identity: validatorIdentity,
totalSlots: slots.length,
slotDistribution: slots,
averageGap: gaps.reduce((sum, gap) => sum + gap, 0) / gaps.length,
maxGap: Math.max(...gaps),
minGap: Math.min(...gaps)
};
}
```
2. **Leader Schedule Monitoring**
```typescript theme={null}
interface ScheduleAlert {
type: 'change' | 'gap' | 'distribution';
message: string;
validator: string;
details: any;
}
class ScheduleMonitor {
private previousSchedule: Map = new Map();
async monitorSchedule(
maxGap: number = 100,
minSlots: number = 10
): Promise {
const schedule = await connection.getLeaderSchedule();
const alerts: ScheduleAlert[] = [];
for (const [validator, slots] of Object.entries(schedule.value)) {
const previousSlots = this.previousSchedule.get(validator) || [];
// Check for significant changes
if (previousSlots.length > 0) {
const change = Math.abs(slots.length - previousSlots.length);
if (change > minSlots) {
alerts.push({
type: 'change',
message: `Validator ${validator} slot count changed by ${change}`,
validator,
details: { previous: previousSlots.length, current: slots.length }
});
}
}
// Check for large gaps
const sortedSlots = [...slots].sort((a, b) => a - b);
for (let i = 1; i < sortedSlots.length; i++) {
const gap = sortedSlots[i] - sortedSlots[i - 1];
if (gap > maxGap) {
alerts.push({
type: 'gap',
message: `Large gap of ${gap} slots found for validator ${validator}`,
validator,
details: { gap, slot1: sortedSlots[i - 1], slot2: sortedSlots[i] }
});
}
}
this.previousSchedule.set(validator, slots);
}
return alerts;
}
}
```
3. **Epoch Planning**
```typescript theme={null}
interface EpochPlan {
currentEpoch: number;
nextEpoch: number;
slotsPerEpoch: number;
validatorSlots: Map;
slotDistribution: Map;
}
async function planEpoch(): Promise {
const schedule = await connection.getLeaderSchedule();
const epochInfo = await connection.getEpochInfo();
const validatorSlots = new Map();
const slotDistribution = new Map();
for (const [validator, slots] of Object.entries(schedule.value)) {
validatorSlots.set(validator, slots.length);
for (const slot of slots) {
if (!slotDistribution.has(slot)) {
slotDistribution.set(slot, []);
}
slotDistribution.get(slot)!.push(validator);
}
}
return {
currentEpoch: epochInfo.epoch,
nextEpoch: epochInfo.epoch + 1,
slotsPerEpoch: epochInfo.slotsInEpoch,
validatorSlots,
slotDistribution
};
}
```
# getMaxRetransmitSlot
Source: https://docs.orbitflare.com/rpc/methods/getMaxRetransmitSlot
Returns the highest slot that can be retransmitted
## Parameters
This method does not take any parameters.
## Response
The highest slot that can be retransmitted
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMaxRetransmitSlot"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get max retransmit slot
const maxRetransmitSlot = await connection.getMaxRetransmitSlot();
console.log('Max retransmit slot:', maxRetransmitSlot);
// Check if a slot can be retransmitted
async function canRetransmitSlot(slot: number) {
const maxRetransmitSlot = await connection.getMaxRetransmitSlot();
return slot <= maxRetransmitSlot;
}
// Get retransmit range
async function getRetransmitRange() {
const maxRetransmitSlot = await connection.getMaxRetransmitSlot();
const firstAvailableBlock = await connection.getFirstAvailableBlock();
return {
startSlot: firstAvailableBlock,
endSlot: maxRetransmitSlot,
totalSlots: maxRetransmitSlot - firstAvailableBlock + 1
};
}
```
## Notes
1. Returns the highest slot that can be retransmitted
2. Used to determine the range of slots available for retransmission
3. The response is immediate as it reads from the current state
4. Slots beyond this value cannot be retransmitted
5. This value can change as the node prunes old data
## Best Practices
1. Use this method to determine retransmission availability
2. Cache results when appropriate to reduce RPC load
3. Consider the impact of slot pruning
4. Monitor for changes in max retransmit slot
5. Use in conjunction with other slot-related methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Slot information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Retransmission Planning**
```typescript theme={null}
interface RetransmitPlan {
startSlot: number;
endSlot: number;
totalSlots: number;
availableSlots: number[];
missingSlots: number[];
}
async function planRetransmission(
targetSlots: number[]
): Promise {
const maxRetransmitSlot = await connection.getMaxRetransmitSlot();
const firstAvailableBlock = await connection.getFirstAvailableBlock();
const availableSlots = targetSlots.filter(
slot => slot >= firstAvailableBlock && slot <= maxRetransmitSlot
);
const missingSlots = targetSlots.filter(
slot => slot < firstAvailableBlock || slot > maxRetransmitSlot
);
return {
startSlot: firstAvailableBlock,
endSlot: maxRetransmitSlot,
totalSlots: maxRetransmitSlot - firstAvailableBlock + 1,
availableSlots,
missingSlots
};
}
```
2. **Slot Availability Monitoring**
```typescript theme={null}
interface SlotAlert {
type: 'pruned' | 'unavailable';
message: string;
slot: number;
maxRetransmitSlot: number;
}
class SlotMonitor {
private previousMaxSlot: number | null = null;
async monitorSlots(
targetSlots: number[]
): Promise {
const maxRetransmitSlot = await connection.getMaxRetransmitSlot();
const alerts: SlotAlert[] = [];
// Check for pruned slots
if (this.previousMaxSlot !== null && maxRetransmitSlot < this.previousMaxSlot) {
alerts.push({
type: 'pruned',
message: `Slots ${maxRetransmitSlot + 1} to ${this.previousMaxSlot} have been pruned`,
slot: this.previousMaxSlot,
maxRetransmitSlot
});
}
// Check target slots availability
for (const slot of targetSlots) {
if (slot > maxRetransmitSlot) {
alerts.push({
type: 'unavailable',
message: `Slot ${slot} is beyond max retransmit slot`,
slot,
maxRetransmitSlot
});
}
}
this.previousMaxSlot = maxRetransmitSlot;
return alerts;
}
}
```
3. **Historical Data Management**
```typescript theme={null}
interface HistoricalData {
availableRange: {
start: number;
end: number;
total: number;
};
prunedSlots: number[];
availableSlots: number[];
metadata: {
timestamp: number;
maxRetransmitSlot: number;
};
}
async function manageHistoricalData(
targetRange: { start: number; end: number }
): Promise {
const maxRetransmitSlot = await connection.getMaxRetransmitSlot();
const firstAvailableBlock = await connection.getFirstAvailableBlock();
const availableSlots = [];
const prunedSlots = [];
for (let slot = targetRange.start; slot <= targetRange.end; slot++) {
if (slot >= firstAvailableBlock && slot <= maxRetransmitSlot) {
availableSlots.push(slot);
} else {
prunedSlots.push(slot);
}
}
return {
availableRange: {
start: firstAvailableBlock,
end: maxRetransmitSlot,
total: maxRetransmitSlot - firstAvailableBlock + 1
},
prunedSlots,
availableSlots,
metadata: {
timestamp: Date.now(),
maxRetransmitSlot
}
};
}
```
# getMaxShredInsertSlot
Source: https://docs.orbitflare.com/rpc/methods/getMaxShredInsertSlot
Returns the highest slot that can be inserted into the shred database
## Parameters
This method does not take any parameters.
## Response
The highest slot that can be inserted into the shred database
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMaxShredInsertSlot"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get max shred insert slot
const maxShredInsertSlot = await connection.getMaxShredInsertSlot();
console.log('Max shred insert slot:', maxShredInsertSlot);
// Check if a slot can be inserted
async function canInsertSlot(slot: number) {
const maxShredInsertSlot = await connection.getMaxShredInsertSlot();
return slot <= maxShredInsertSlot;
}
// Get insert range
async function getInsertRange() {
const maxShredInsertSlot = await connection.getMaxShredInsertSlot();
const firstAvailableBlock = await connection.getFirstAvailableBlock();
return {
startSlot: firstAvailableBlock,
endSlot: maxShredInsertSlot,
totalSlots: maxShredInsertSlot - firstAvailableBlock + 1
};
}
```
## Notes
1. Returns the highest slot that can be inserted into the shred database
2. Used to determine the range of slots available for shred insertion
3. The response is immediate as it reads from the current state
4. Slots beyond this value cannot be inserted
5. This value can change as the node processes new slots
## Best Practices
1. Use this method to determine shred insertion availability
2. Cache results when appropriate to reduce RPC load
3. Consider the impact of slot processing
4. Monitor for changes in max shred insert slot
5. Use in conjunction with other slot-related methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32007 | Slot information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Shred Insertion Planning**
```typescript theme={null}
interface InsertPlan {
startSlot: number;
endSlot: number;
totalSlots: number;
availableSlots: number[];
unavailableSlots: number[];
}
async function planShredInsertion(
targetSlots: number[]
): Promise {
const maxShredInsertSlot = await connection.getMaxShredInsertSlot();
const firstAvailableBlock = await connection.getFirstAvailableBlock();
const availableSlots = targetSlots.filter(
slot => slot >= firstAvailableBlock && slot <= maxShredInsertSlot
);
const unavailableSlots = targetSlots.filter(
slot => slot < firstAvailableBlock || slot > maxShredInsertSlot
);
return {
startSlot: firstAvailableBlock,
endSlot: maxShredInsertSlot,
totalSlots: maxShredInsertSlot - firstAvailableBlock + 1,
availableSlots,
unavailableSlots
};
}
```
2. **Slot Processing Monitoring**
```typescript theme={null}
interface ProcessingAlert {
type: 'lag' | 'gap' | 'unavailable';
message: string;
slot: number;
maxShredInsertSlot: number;
}
class ProcessingMonitor {
private previousMaxSlot: number | null = null;
async monitorProcessing(
targetSlots: number[]
): Promise {
const maxShredInsertSlot = await connection.getMaxShredInsertSlot();
const alerts: ProcessingAlert[] = [];
// Check for processing lag
if (this.previousMaxSlot !== null) {
const expectedProgress = this.previousMaxSlot + 1;
if (maxShredInsertSlot < expectedProgress) {
alerts.push({
type: 'lag',
message: `Processing lag detected: expected ${expectedProgress}, got ${maxShredInsertSlot}`,
slot: maxShredInsertSlot,
maxShredInsertSlot
});
}
}
// Check target slots availability
for (const slot of targetSlots) {
if (slot > maxShredInsertSlot) {
alerts.push({
type: 'unavailable',
message: `Slot ${slot} is beyond max shred insert slot`,
slot,
maxShredInsertSlot
});
}
}
this.previousMaxSlot = maxShredInsertSlot;
return alerts;
}
}
```
3. **Shred Database Management**
```typescript theme={null}
interface ShredDatabaseMetrics {
availableRange: {
start: number;
end: number;
total: number;
};
processingRate: number;
lag: number;
metadata: {
timestamp: number;
maxShredInsertSlot: number;
};
}
class ShredDatabaseManager {
private previousMetrics: {
timestamp: number;
maxSlot: number;
} | null = null;
async getMetrics(): Promise {
const maxShredInsertSlot = await connection.getMaxShredInsertSlot();
const firstAvailableBlock = await connection.getFirstAvailableBlock();
const currentTime = Date.now();
let processingRate = 0;
let lag = 0;
if (this.previousMetrics !== null) {
const timeDiff = (currentTime - this.previousMetrics.timestamp) / 1000; // seconds
const slotDiff = maxShredInsertSlot - this.previousMetrics.maxSlot;
processingRate = slotDiff / timeDiff;
const currentSlot = await connection.getSlot();
lag = currentSlot - maxShredInsertSlot;
}
this.previousMetrics = {
timestamp: currentTime,
maxSlot: maxShredInsertSlot
};
return {
availableRange: {
start: firstAvailableBlock,
end: maxShredInsertSlot,
total: maxShredInsertSlot - firstAvailableBlock + 1
},
processingRate,
lag,
metadata: {
timestamp: currentTime,
maxShredInsertSlot
}
};
}
}
```
# getMinimumBalanceForRentExemption
Source: https://docs.orbitflare.com/rpc/methods/getMinimumBalanceForRentExemption
Returns the minimum balance required for an account to be rent exempt
## Parameters
The length of the account data in bytes
Commitment level (processed, confirmed, finalized)
## Response
The minimum balance required for rent exemption in lamports
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMinimumBalanceForRentExemption",
"params": [1000]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get minimum balance for rent exemption
const dataLength = 1000; // bytes
const minBalance = await connection.getMinimumBalanceForRentExemption(dataLength);
console.log('Minimum balance:', minBalance);
// Calculate account creation cost
async function calculateAccountCost(
dataLength: number,
owner: string
) {
const minBalance = await connection.getMinimumBalanceForRentExemption(dataLength);
const rentExempt = minBalance;
return {
dataLength,
minBalance,
rentExempt,
owner
};
}
// Check if account has sufficient balance
async function hasSufficientBalance(
account: string,
dataLength: number
) {
const [balance, minBalance] = await Promise.all([
connection.getBalance(account),
connection.getMinimumBalanceForRentExemption(dataLength)
]);
return {
account,
currentBalance: balance,
requiredBalance: minBalance,
hasSufficientBalance: balance >= minBalance,
difference: balance - minBalance
};
}
```
## Notes
1. Returns the minimum balance required for an account to be rent exempt
2. The balance is returned in lamports
3. The required balance depends on the account's data length
4. The response is immediate as it reads from the current state
5. This value can change with network upgrades
## Best Practices
1. Use this method to determine account creation costs
2. Cache results when appropriate to reduce RPC load
3. Consider the impact of data length on costs
4. Monitor for changes in rent exemption requirements
5. Use in conjunction with other account-related methods
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check dataLength parameter |
| -32007 | Balance information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Account Cost Analysis**
```typescript theme={null}
interface AccountCost {
dataLength: number;
minBalance: number;
rentExempt: number;
owner: string;
metadata: {
timestamp: number;
commitment: string;
};
}
async function analyzeAccountCost(
dataLength: number,
owner: string,
commitment: string = 'confirmed'
): Promise {
const minBalance = await connection.getMinimumBalanceForRentExemption(
dataLength,
{ commitment }
);
return {
dataLength,
minBalance,
rentExempt: minBalance,
owner,
metadata: {
timestamp: Date.now(),
commitment
}
};
}
```
2. **Balance Monitoring**
```typescript theme={null}
interface BalanceAlert {
type: 'insufficient' | 'warning' | 'sufficient';
message: string;
account: string;
currentBalance: number;
requiredBalance: number;
difference: number;
}
class BalanceMonitor {
private previousBalances: Map = new Map();
async monitorBalance(
account: string,
dataLength: number,
warningThreshold: number = 0.1 // 10% above minimum
): Promise {
const [balance, minBalance] = await Promise.all([
connection.getBalance(account),
connection.getMinimumBalanceForRentExemption(dataLength)
]);
const alerts: BalanceAlert[] = [];
const difference = balance - minBalance;
const warningLevel = minBalance * warningThreshold;
if (difference < 0) {
alerts.push({
type: 'insufficient',
message: `Account ${account} has insufficient balance for rent exemption`,
account,
currentBalance: balance,
requiredBalance: minBalance,
difference
});
} else if (difference < warningLevel) {
alerts.push({
type: 'warning',
message: `Account ${account} balance is close to minimum required`,
account,
currentBalance: balance,
requiredBalance: minBalance,
difference
});
} else {
alerts.push({
type: 'sufficient',
message: `Account ${account} has sufficient balance`,
account,
currentBalance: balance,
requiredBalance: minBalance,
difference
});
}
this.previousBalances.set(account, balance);
return alerts;
}
}
```
3. **Account Planning**
```typescript theme={null}
interface AccountPlan {
accounts: Array<{
purpose: string;
dataLength: number;
minBalance: number;
estimatedUsage: string;
}>;
totalCost: number;
recommendations: string[];
}
async function planAccounts(
accountSpecs: Array<{
purpose: string;
dataLength: number;
estimatedUsage: string;
}>
): Promise {
const accounts = await Promise.all(
accountSpecs.map(async spec => {
const minBalance = await connection.getMinimumBalanceForRentExemption(
spec.dataLength
);
return {
...spec,
minBalance
};
})
);
const totalCost = accounts.reduce(
(sum, account) => sum + account.minBalance,
0
);
const recommendations = [];
// Check for potential optimizations
const largeAccounts = accounts.filter(
account => account.dataLength > 10000
);
if (largeAccounts.length > 0) {
recommendations.push(
'Consider optimizing data storage for large accounts'
);
}
const expensiveAccounts = accounts.filter(
account => account.minBalance > 1e9 // More than 1 SOL
);
if (expensiveAccounts.length > 0) {
recommendations.push(
'Review high-cost accounts for potential optimizations'
);
}
return {
accounts,
totalCost,
recommendations
};
}
```
# getMultipleAccounts
Source: https://docs.orbitflare.com/rpc/methods/getMultipleAccounts
Returns the account information for multiple accounts
## Parameters
Array of account public keys (base-58 encoded)
Commitment level (processed, confirmed, finalized)
Encoding format for account data (base58, base64, jsonParsed)
Offset into account data to start reading
Length of data to read
## Response
Slot at which the request was processed
Array of account information objects or null for non-existent accounts
Account data in the specified encoding
Whether the account is executable
Account balance in lamports
Account owner (base-58 encoded)
Epoch at which the account will next owe rent
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMultipleAccounts",
"params": [
["ACCOUNT1", "ACCOUNT2", "ACCOUNT3"]
]
}'
```
### Request with Encoding
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getMultipleAccounts",
"params": [
["ACCOUNT1", "ACCOUNT2"],
{
"encoding": "base64",
"dataSlice": {
"offset": 0,
"length": 100
}
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get multiple accounts
const accounts = await connection.getMultipleAccounts([
new PublicKey('ACCOUNT1'),
new PublicKey('ACCOUNT2')
]);
console.log('Accounts:', accounts.value);
// Get account data slices
async function getAccountDataSlices(
pubkeys: PublicKey[],
offset: number,
length: number
) {
const accounts = await connection.getMultipleAccounts(pubkeys, {
encoding: 'base64',
dataSlice: { offset, length }
});
return accounts.value.map((account, index) => ({
pubkey: pubkeys[index].toBase58(),
exists: account !== null,
data: account?.data[0],
lamports: account?.lamports
}));
}
```
## Notes
1. Returns information for multiple accounts in a single request
2. Non-existent accounts are returned as null
3. Account data can be sliced to reduce response size
4. The response is immediate as it reads from the current state
5. Different encodings can be specified for account data
## Best Practices
1. Use this method to efficiently fetch multiple accounts
2. Consider using dataSlice to reduce response size
3. Cache results when appropriate to reduce RPC load
4. Handle null values for non-existent accounts
5. Use appropriate encoding based on your needs
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check pubkeys and config parameters |
| -32007 | Account information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Account Batch Processing**
```typescript theme={null}
interface AccountBatch {
accounts: Array<{
pubkey: string;
exists: boolean;
lamports: number;
owner: string;
executable: boolean;
}>;
totalLamports: number;
metadata: {
timestamp: number;
slot: number;
};
}
async function processAccountBatch(
pubkeys: PublicKey[],
chunkSize: number = 100
): Promise {
const accounts: AccountBatch['accounts'] = [];
let totalLamports = 0;
for (let i = 0; i < pubkeys.length; i += chunkSize) {
const chunk = pubkeys.slice(i, i + chunkSize);
const result = await connection.getMultipleAccounts(chunk);
chunk.forEach((pubkey, index) => {
const account = result.value[index];
const exists = account !== null;
accounts.push({
pubkey: pubkey.toBase58(),
exists,
lamports: account?.lamports || 0,
owner: account?.owner.toBase58() || '',
executable: account?.executable || false
});
if (exists) {
totalLamports += account.lamports;
}
});
}
return {
accounts,
totalLamports,
metadata: {
timestamp: Date.now(),
slot: accounts[0]?.slot || 0
}
};
}
```
2. **Account Data Analysis**
```typescript theme={null}
interface AccountDataAnalysis {
pubkey: string;
dataSize: number;
dataHash: string;
lamports: number;
owner: string;
metadata: {
timestamp: number;
slot: number;
};
}
async function analyzeAccountData(
pubkeys: PublicKey[],
dataSlice?: { offset: number; length: number }
): Promise {
const accounts = await connection.getMultipleAccounts(pubkeys, {
encoding: 'base64',
dataSlice
});
return accounts.value.map((account, index) => {
const pubkey = pubkeys[index].toBase58();
if (!account) {
return {
pubkey,
dataSize: 0,
dataHash: '',
lamports: 0,
owner: '',
metadata: {
timestamp: Date.now(),
slot: accounts.context.slot
}
};
}
const data = account.data[0];
const dataSize = Buffer.from(data, 'base64').length;
return {
pubkey,
dataSize,
dataHash: require('crypto')
.createHash('sha256')
.update(data)
.digest('hex'),
lamports: account.lamports,
owner: account.owner.toBase58(),
metadata: {
timestamp: Date.now(),
slot: accounts.context.slot
}
};
});
}
```
3. **Account Monitoring**
```typescript theme={null}
interface AccountChange {
pubkey: string;
type: 'created' | 'deleted' | 'modified';
previous: any;
current: any;
difference: number;
}
class AccountMonitor {
private previousAccounts: Map = new Map();
async monitorAccounts(
pubkeys: PublicKey[],
interval: number = 5000
): Promise {
const accounts = await connection.getMultipleAccounts(pubkeys);
const changes: AccountChange[] = [];
pubkeys.forEach((pubkey, index) => {
const address = pubkey.toBase58();
const current = accounts.value[index];
const previous = this.previousAccounts.get(address);
if (!previous && current) {
changes.push({
pubkey: address,
type: 'created',
previous: null,
current,
difference: current.lamports
});
} else if (previous && !current) {
changes.push({
pubkey: address,
type: 'deleted',
previous,
current: null,
difference: -previous.lamports
});
} else if (previous && current) {
const difference = current.lamports - previous.lamports;
if (difference !== 0 || current.data[0] !== previous.data[0]) {
changes.push({
pubkey: address,
type: 'modified',
previous,
current,
difference
});
}
}
this.previousAccounts.set(address, current);
});
return changes;
}
}
```
# getProgramAccounts
Source: https://docs.orbitflare.com/rpc/methods/getProgramAccounts
Returns all accounts owned by a program
## Parameters
Program public key (base-58 encoded)
Commitment level (processed, confirmed, finalized)
Encoding format for account data (base58, base64, jsonParsed)
Offset into account data to start reading
Length of data to read
Array of filter objects to apply to accounts
Offset into account data to compare
Base-58 encoded bytes to match
Account data size to match
## Response
Array of account information objects
Account public key (base-58 encoded)
Account data in the specified encoding
Whether the account is executable
Account balance in lamports
Account owner (base-58 encoded)
Epoch at which the account will next owe rent
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getProgramAccounts",
"params": [
"PROGRAM_ID"
]
}'
```
### Request with Filters
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getProgramAccounts",
"params": [
"PROGRAM_ID",
{
"filters": [
{
"dataSize": 165
},
{
"memcmp": {
"offset": 0,
"bytes": "BASE58_ENCODED_BYTES"
}
}
]
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get all program accounts
const accounts = await connection.getProgramAccounts(
new PublicKey('PROGRAM_ID')
);
console.log('Accounts:', accounts);
// Get filtered program accounts
async function getFilteredProgramAccounts(
programId: PublicKey,
filters: any[]
) {
const accounts = await connection.getProgramAccounts(programId, {
filters
});
return accounts.map(({ pubkey, account }) => ({
pubkey: pubkey.toBase58(),
data: account.data,
lamports: account.lamports,
owner: account.owner.toBase58(),
executable: account.executable
}));
}
```
## Notes
1. Returns all accounts owned by a specific program
2. Filters can be applied to narrow down results
3. Account data can be sliced to reduce response size
4. The response is immediate as it reads from the current state
5. Different encodings can be specified for account data
## Best Practices
1. Use filters to efficiently query specific accounts
2. Consider using dataSlice to reduce response size
3. Cache results when appropriate to reduce RPC load
4. Handle large result sets with pagination
5. Use appropriate encoding based on your needs
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check programId and config parameters |
| -32007 | Account information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Program Account Analysis**
```typescript theme={null}
interface ProgramAccountAnalysis {
pubkey: string;
dataSize: number;
dataHash: string;
lamports: number;
metadata: {
timestamp: number;
};
}
async function analyzeProgramAccounts(
programId: PublicKey,
filters?: any[]
): Promise {
const accounts = await connection.getProgramAccounts(programId, {
filters,
encoding: 'base64'
});
return accounts.map(({ pubkey, account }) => {
const data = account.data[0];
const dataSize = Buffer.from(data, 'base64').length;
return {
pubkey: pubkey.toBase58(),
dataSize,
dataHash: require('crypto')
.createHash('sha256')
.update(data)
.digest('hex'),
lamports: account.lamports,
metadata: {
timestamp: Date.now()
}
};
});
}
```
2. **Program Account Monitoring**
```typescript theme={null}
interface ProgramAccountChange {
pubkey: string;
type: 'created' | 'deleted' | 'modified';
previous: any;
current: any;
difference: number;
}
class ProgramAccountMonitor {
private previousAccounts: Map = new Map();
async monitorProgramAccounts(
programId: PublicKey,
filters?: any[],
interval: number = 5000
): Promise {
const accounts = await connection.getProgramAccounts(programId, {
filters
});
const changes: ProgramAccountChange[] = [];
accounts.forEach(({ pubkey, account }) => {
const address = pubkey.toBase58();
const current = account;
const previous = this.previousAccounts.get(address);
if (!previous && current) {
changes.push({
pubkey: address,
type: 'created',
previous: null,
current,
difference: current.lamports
});
} else if (previous && !current) {
changes.push({
pubkey: address,
type: 'deleted',
previous,
current: null,
difference: -previous.lamports
});
} else if (previous && current) {
const difference = current.lamports - previous.lamports;
if (difference !== 0 || current.data[0] !== previous.data[0]) {
changes.push({
pubkey: address,
type: 'modified',
previous,
current,
difference
});
}
}
this.previousAccounts.set(address, current);
});
return changes;
}
}
```
3. **Program Account Pagination**
```typescript theme={null}
interface ProgramAccountPage {
accounts: Array<{
pubkey: string;
data: any;
lamports: number;
}>;
total: number;
page: number;
pageSize: number;
metadata: {
timestamp: number;
};
}
async function getProgramAccountPage(
programId: PublicKey,
page: number = 1,
pageSize: number = 100,
filters?: any[]
): Promise {
const accounts = await connection.getProgramAccounts(programId, {
filters,
encoding: 'base64'
});
const start = (page - 1) * pageSize;
const end = start + pageSize;
const pageAccounts = accounts.slice(start, end);
return {
accounts: pageAccounts.map(({ pubkey, account }) => ({
pubkey: pubkey.toBase58(),
data: account.data[0],
lamports: account.lamports
})),
total: accounts.length,
page,
pageSize,
metadata: {
timestamp: Date.now()
}
};
}
```
# getRecentBlockhash
Source: https://docs.orbitflare.com/rpc/methods/getRecentBlockhash
[DEPRECATED] Returns a recent blockhash and its fee schedule
This method is deprecated. Please use [`getLatestBlockhash`](/rpc/methods/getLatestBlockhash) instead, which provides more accurate and reliable blockhash information.
Returns a recent blockhash and its fee schedule. This method will be removed in a future release.
## Parameters
Commitment level (processed, confirmed, finalized)
## Response
Slot at which the request was processed
Recent blockhash (base-58 encoded)
Fee in lamports per signature
## Migration Guide
To migrate from `getRecentBlockhash` to `getLatestBlockhash`:
1. Replace method calls:
```diff theme={null}
- const { blockhash, feeCalculator } = await connection.getRecentBlockhash();
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
```
2. Update fee calculations:
```typescript theme={null}
// Old way
const fee = feeCalculator.lamportsPerSignature * numSignatures;
// New way
const fee = await connection.getFeeForMessage(message);
```
## Code Examples
### Basic Request (Legacy)
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getRecentBlockhash",
"params": []
}'
```
### Request with Commitment (Legacy)
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getRecentBlockhash",
"params": [
{
"commitment": "confirmed"
}
]
}'
```
### Using web3.js (Legacy)
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get recent blockhash (deprecated)
const { value: { blockhash, feeCalculator } } = await connection.getRecentBlockhash();
console.log('Blockhash:', blockhash);
console.log('Fee per signature:', feeCalculator.lamportsPerSignature);
```
## Notes
1. This method is deprecated and will be removed in a future release
2. Use `getLatestBlockhash` for new development
3. The blockhash is valid for a limited time (typically 150 slots)
4. Different commitment levels can be specified
5. Consider updating existing code to use the new methods
## Best Practices
1. Migrate to `getLatestBlockhash` for new development
2. Use `getFeeForMessage` for fee calculations
3. Monitor for blockhash expiration using `lastValidBlockHeight`
4. Handle network errors and retry when appropriate
5. Use appropriate commitment level based on your needs
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check config parameters |
| -32007 | Blockhash information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Transaction Fee Calculation**
```typescript theme={null}
interface TransactionFee {
blockhash: string;
feePerSignature: number;
totalFee: number;
metadata: {
timestamp: number;
slot: number;
};
}
async function calculateTransactionFees(
numSignatures: number
): Promise {
const { context, value } = await connection.getRecentBlockhash();
return {
blockhash: value.blockhash,
feePerSignature: value.feeCalculator.lamportsPerSignature,
totalFee: numSignatures * value.feeCalculator.lamportsPerSignature,
metadata: {
timestamp: Date.now(),
slot: context.slot
}
};
}
```
2. **Blockhash Monitoring**
```typescript theme={null}
interface BlockhashInfo {
blockhash: string;
slot: number;
feePerSignature: number;
metadata: {
timestamp: number;
age: number;
};
}
class BlockhashMonitor {
private currentBlockhash: string | null = null;
private currentSlot: number | null = null;
async monitorBlockhash(
interval: number = 5000
): Promise {
const { context, value } = await connection.getRecentBlockhash();
if (this.currentBlockhash === value.blockhash) {
return null;
}
this.currentBlockhash = value.blockhash;
this.currentSlot = context.slot;
return {
blockhash: value.blockhash,
slot: context.slot,
feePerSignature: value.feeCalculator.lamportsPerSignature,
metadata: {
timestamp: Date.now(),
age: 0
}
};
}
}
```
3. **Transaction Planning**
```typescript theme={null}
interface TransactionPlan {
blockhash: string;
feePerSignature: number;
estimatedFees: {
single: number;
batch: number;
};
metadata: {
timestamp: number;
slot: number;
};
}
async function planTransactions(
numTransactions: number,
signaturesPerTransaction: number
): Promise {
const { context, value } = await connection.getRecentBlockhash();
const feePerSignature = value.feeCalculator.lamportsPerSignature;
return {
blockhash: value.blockhash,
feePerSignature,
estimatedFees: {
single: signaturesPerTransaction * feePerSignature,
batch: numTransactions * signaturesPerTransaction * feePerSignature
},
metadata: {
timestamp: Date.now(),
slot: context.slot
}
};
}
```
# getRecentPerformanceSamples
Source: https://docs.orbitflare.com/rpc/methods/getRecentPerformanceSamples
Returns recent performance samples
## Parameters
Number of samples to return (default: 720)
## Response
Array of performance sample objects
Slot number
Number of slots in the sample
Number of transactions in the sample
Sample period in seconds
Number of non-vote transactions
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getRecentPerformanceSamples",
"params": []
}'
```
### Request with Limit
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getRecentPerformanceSamples",
"params": [
100
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get recent performance samples
const samples = await connection.getRecentPerformanceSamples();
console.log('Performance samples:', samples);
// Calculate average TPS
function calculateAverageTPS(samples: any[]): number {
const totalTransactions = samples.reduce(
(sum, sample) => sum + sample.numTransactions,
0
);
const totalSeconds = samples.reduce(
(sum, sample) => sum + sample.samplePeriodSecs,
0
);
return totalTransactions / totalSeconds;
}
```
## Notes
1. Returns recent performance samples from the network
2. Each sample covers a period of time (typically 60 seconds)
3. Samples include transaction counts and slot information
4. The response is immediate as it reads from the current state
5. The default limit is 720 samples (approximately 12 hours)
## Best Practices
1. Use appropriate limit based on your analysis needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for significant changes in performance
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check limit parameter |
| -32007 | Performance samples unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Performance Analysis**
```typescript theme={null}
interface PerformanceMetrics {
averageTPS: number;
peakTPS: number;
totalTransactions: number;
totalSlots: number;
metadata: {
timestamp: number;
sampleCount: number;
};
}
async function analyzePerformance(
limit: number = 720
): Promise {
const samples = await connection.getRecentPerformanceSamples(limit);
const totalTransactions = samples.reduce(
(sum, sample) => sum + sample.numTransactions,
0
);
const totalSeconds = samples.reduce(
(sum, sample) => sum + sample.samplePeriodSecs,
0
);
const peakTPS = Math.max(
...samples.map(sample => sample.numTransactions / sample.samplePeriodSecs)
);
return {
averageTPS: totalTransactions / totalSeconds,
peakTPS,
totalTransactions,
totalSlots: samples.reduce((sum, sample) => sum + sample.numSlots, 0),
metadata: {
timestamp: Date.now(),
sampleCount: samples.length
}
};
}
```
2. **Performance Monitoring**
```typescript theme={null}
interface PerformanceAlert {
type: 'high_tps' | 'low_tps' | 'anomaly';
currentTPS: number;
threshold: number;
metadata: {
timestamp: number;
slot: number;
};
}
class PerformanceMonitor {
private previousTPS: number | null = null;
private readonly threshold = 0.2; // 20% change threshold
async monitorPerformance(
interval: number = 60000
): Promise {
const samples = await connection.getRecentPerformanceSamples(1);
const currentSample = samples[0];
const currentTPS = currentSample.numTransactions / currentSample.samplePeriodSecs;
if (this.previousTPS === null) {
this.previousTPS = currentTPS;
return null;
}
const change = Math.abs(currentTPS - this.previousTPS) / this.previousTPS;
if (change > this.threshold) {
const alert: PerformanceAlert = {
type: change > 0 ? 'high_tps' : 'low_tps',
currentTPS,
threshold: this.threshold,
metadata: {
timestamp: Date.now(),
slot: currentSample.slot
}
};
this.previousTPS = currentTPS;
return alert;
}
return null;
}
}
```
3. **Performance History**
```typescript theme={null}
interface PerformanceHistory {
samples: Array<{
slot: number;
tps: number;
nonVoteTPS: number;
timestamp: number;
}>;
summary: {
averageTPS: number;
peakTPS: number;
totalTransactions: number;
};
metadata: {
startTime: number;
endTime: number;
sampleCount: number;
};
}
async function getPerformanceHistory(
limit: number = 720
): Promise {
const samples = await connection.getRecentPerformanceSamples(limit);
const processedSamples = samples.map(sample => ({
slot: sample.slot,
tps: sample.numTransactions / sample.samplePeriodSecs,
nonVoteTPS: sample.numNonVoteTransaction / sample.samplePeriodSecs,
timestamp: Date.now() - (samples.length - samples.indexOf(sample)) * sample.samplePeriodSecs * 1000
}));
const totalTransactions = samples.reduce(
(sum, sample) => sum + sample.numTransactions,
0
);
const totalSeconds = samples.reduce(
(sum, sample) => sum + sample.samplePeriodSecs,
0
);
return {
samples: processedSamples,
summary: {
averageTPS: totalTransactions / totalSeconds,
peakTPS: Math.max(...processedSamples.map(sample => sample.tps)),
totalTransactions
},
metadata: {
startTime: processedSamples[0].timestamp,
endTime: processedSamples[processedSamples.length - 1].timestamp,
sampleCount: samples.length
}
};
}
```
# getRecentPrioritizationFees
Source: https://docs.orbitflare.com/rpc/methods/getRecentPrioritizationFees
Returns a list of recent prioritization fees from recent blocks
## Parameters
An array of account addresses (as base-58 encoded strings). If provided, the response will be filtered to only include prioritization fees paid by one of the addresses.
## Response
The slot in which the fee was observed
The fee amount in micro-lamports (LAMPORTS\_PER\_SOL/1000000)
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getRecentPrioritizationFees",
"params": [
["CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY"]
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const address = 'CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY';
const fees = await connection.getRecentPrioritizationFees([address]);
console.log(fees);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
address = "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY"
response = client.get_recent_prioritization_fees([address])
print(response)
```
## Notes
1. Returns a list of recent prioritization fees from blocks
2. Useful for estimating fees for priority transactions
3. Fees are reported in micro-lamports (LAMPORTS\_PER\_SOL/1000000)
4. If addresses parameter is provided, results are filtered to those accounts
5. The response can be used to calculate appropriate prioritization fees
## Best Practices
1. Use this method to determine appropriate priority fees during network congestion
2. Filter by specific addresses if you only care about fees paid by certain accounts
3. Calculate an appropriate fee based on average or percentile of recent fees
4. Refresh fee data frequently during high network activity
5. Handle network errors and retry when appropriate
# getSignatureStatuses
Source: https://docs.orbitflare.com/rpc/methods/getSignatureStatuses
Returns the statuses of a list of signatures
## Parameters
An array of transaction signatures to confirm, as base-58 encoded strings
If true, a Solana node will search its ledger cache for any signatures not found in the recent status cache
## Response
The slot this request was processed in
The slot the transaction was processed
Number of blocks since signature confirmation, null if rooted or not found
Error if transaction failed, null if transaction succeeded
The transaction's cluster confirmation status; either "processed", "confirmed", or "finalized"
## Code Examples
### Basic Request
```bash theme={null}
curl http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSignatureStatuses",
"params": [
[
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
"5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"
]
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
const signatures = [
'5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW',
'5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7'
];
const statuses = await connection.getSignatureStatuses(signatures);
console.log(statuses);
```
### Using Python
```python theme={null}
from solana.rpc.api import Client
client = Client("http://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY")
signatures = [
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
"5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"
]
response = client.get_signature_statuses(signatures)
print(response)
```
## Notes
1. Returns the statuses of a list of transaction signatures
2. By default, only searches recent status cache (last \~5 minutes of transactions)
3. Setting searchTransactionHistory to true will search further back in the ledger
4. null values in the response array indicate signatures that could not be found
5. confirmationStatus indicates how finalized a transaction is on the network
## Best Practices
1. Use this method to check the status of recently submitted transactions
2. Pass multiple signatures in a single request to reduce network overhead
3. Set searchTransactionHistory to true only when needed (performance impact)
4. Handle null values in the response array appropriately
5. Check confirmationStatus to determine transaction finality level
# getSignaturesForAddress
Source: https://docs.orbitflare.com/rpc/methods/getSignaturesForAddress
Returns signatures for confirmed transactions for an address
## Parameters
Account address (base-58 encoded)
Maximum number of signatures to return (default: 1000)
Start searching backwards from this signature
Search until this signature
Commitment level (processed, confirmed, finalized)
## Response
Array of signature information objects
Transaction signature (base-58 encoded)
Slot in which the transaction was processed
Error if transaction failed, null if transaction succeeded
Memo associated with the transaction
Unix timestamp of when the transaction was processed
Transaction confirmation status
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSignaturesForAddress",
"params": [
"ADDRESS"
]
}'
```
### Request with Config
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSignaturesForAddress",
"params": [
"ADDRESS",
{
"limit": 100,
"before": "SIGNATURE",
"commitment": "confirmed"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get signatures for address
const signatures = await connection.getSignaturesForAddress(
new PublicKey('ADDRESS')
);
console.log('Signatures:', signatures);
// Get paginated signatures
async function getPaginatedSignatures(
address: PublicKey,
limit: number = 100,
before?: string
) {
const signatures = await connection.getSignaturesForAddress(address, {
limit,
before
});
return signatures.map(signature => ({
signature: signature.signature,
slot: signature.slot,
success: !signature.err,
timestamp: signature.blockTime,
status: signature.confirmationStatus
}));
}
```
## Notes
1. Returns confirmed transaction signatures for an address
2. Results are returned in reverse chronological order
3. Pagination can be achieved using the `before` parameter
4. The response is immediate as it reads from the current state
5. Different commitment levels can be specified
## Best Practices
1. Use appropriate limit based on your needs
2. Implement pagination for large result sets
3. Cache results when appropriate to reduce RPC load
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check address and config parameters |
| -32007 | Signature information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Transaction History**
```typescript theme={null}
interface TransactionHistory {
transactions: Array<{
signature: string;
slot: number;
success: boolean;
timestamp: number;
status: string;
}>;
metadata: {
total: number;
startTime: number;
endTime: number;
};
}
async function getTransactionHistory(
address: PublicKey,
limit: number = 100
): Promise {
const signatures = await connection.getSignaturesForAddress(address, {
limit
});
return {
transactions: signatures.map(signature => ({
signature: signature.signature,
slot: signature.slot,
success: !signature.err,
timestamp: signature.blockTime || 0,
status: signature.confirmationStatus || 'unknown'
})),
metadata: {
total: signatures.length,
startTime: signatures[0]?.blockTime || 0,
endTime: signatures[signatures.length - 1]?.blockTime || 0
}
};
}
```
2. **Transaction Monitoring**
```typescript theme={null}
interface TransactionAlert {
type: 'new' | 'failed' | 'status_change';
signature: string;
previousStatus: string | null;
currentStatus: string;
metadata: {
timestamp: number;
slot: number;
};
}
class TransactionMonitor {
private previousSignatures: Map = new Map();
async monitorTransactions(
address: PublicKey,
interval: number = 5000
): Promise {
const signatures = await connection.getSignaturesForAddress(address, {
limit: 10
});
const alerts: TransactionAlert[] = [];
signatures.forEach(signature => {
const previous = this.previousSignatures.get(signature.signature);
if (!previous) {
alerts.push({
type: 'new',
signature: signature.signature,
previousStatus: null,
currentStatus: signature.confirmationStatus || 'unknown',
metadata: {
timestamp: Date.now(),
slot: signature.slot
}
});
} else if (previous.confirmationStatus !== signature.confirmationStatus) {
alerts.push({
type: 'status_change',
signature: signature.signature,
previousStatus: previous.confirmationStatus,
currentStatus: signature.confirmationStatus || 'unknown',
metadata: {
timestamp: Date.now(),
slot: signature.slot
}
});
}
this.previousSignatures.set(signature.signature, signature);
});
return alerts;
}
}
```
3. **Transaction Analysis**
```typescript theme={null}
interface TransactionAnalysis {
total: number;
successful: number;
failed: number;
statusDistribution: Record;
timeDistribution: {
hourly: Record;
daily: Record;
};
metadata: {
startTime: number;
endTime: number;
duration: number;
};
}
async function analyzeTransactions(
address: PublicKey,
limit: number = 1000
): Promise {
const signatures = await connection.getSignaturesForAddress(address, {
limit
});
const statusDistribution: Record = {};
const hourlyDistribution: Record = {};
const dailyDistribution: Record = {};
let successful = 0;
let failed = 0;
signatures.forEach(signature => {
const status = signature.confirmationStatus || 'unknown';
statusDistribution[status] = (statusDistribution[status] || 0) + 1;
if (signature.err) {
failed++;
} else {
successful++;
}
if (signature.blockTime) {
const date = new Date(signature.blockTime * 1000);
const hour = date.getHours();
const day = date.getDay();
hourlyDistribution[hour] = (hourlyDistribution[hour] || 0) + 1;
dailyDistribution[day] = (dailyDistribution[day] || 0) + 1;
}
});
return {
total: signatures.length,
successful,
failed,
statusDistribution,
timeDistribution: {
hourly: hourlyDistribution,
daily: dailyDistribution
},
metadata: {
startTime: signatures[0]?.blockTime || 0,
endTime: signatures[signatures.length - 1]?.blockTime || 0,
duration: signatures.length > 1
? (signatures[0]?.blockTime || 0) - (signatures[signatures.length - 1]?.blockTime || 0)
: 0
}
};
}
```
# getSlot
Source: https://docs.orbitflare.com/rpc/methods/getSlot
Returns the current slot of the node
## Parameters
Commitment level (processed, confirmed, finalized)
## Response
Current slot number
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSlot",
"params": []
}'
```
### Request with Commitment
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSlot",
"params": [
{
"commitment": "confirmed"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get current slot
const slot = await connection.getSlot();
console.log('Current slot:', slot);
// Get slot with commitment
async function getSlotWithCommitment(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed'
) {
const slot = await connection.getSlot(commitment);
return {
slot,
commitment,
timestamp: Date.now()
};
}
```
## Notes
1. Returns the current slot of the node
2. The slot number increases as new blocks are produced
3. Different commitment levels can be specified
4. The response is immediate as it reads from the current state
5. The slot number is used for various time-based operations
## Best Practices
1. Cache the slot number when appropriate
2. Consider using websocket subscription for real-time updates
3. Handle network errors and retry when appropriate
4. Use appropriate commitment level based on your needs
5. Monitor slot progression for time-sensitive operations
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check config parameters |
| -32007 | Slot information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Slot Monitoring**
```typescript theme={null}
interface SlotInfo {
slot: number;
commitment: string;
metadata: {
timestamp: number;
previousSlot: number | null;
slotProgression: number;
};
}
class SlotMonitor {
private previousSlot: number | null = null;
async monitorSlot(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed',
interval: number = 1000
): Promise {
const slot = await connection.getSlot(commitment);
const timestamp = Date.now();
const info: SlotInfo = {
slot,
commitment,
metadata: {
timestamp,
previousSlot: this.previousSlot,
slotProgression: this.previousSlot !== null
? slot - this.previousSlot
: 0
}
};
this.previousSlot = slot;
return info;
}
}
```
2. **Slot Analysis**
```typescript theme={null}
interface SlotAnalysis {
currentSlot: number;
slotProgression: {
average: number;
min: number;
max: number;
samples: number;
};
timeDistribution: {
slotsPerSecond: number;
slotsPerMinute: number;
slotsPerHour: number;
};
metadata: {
startTime: number;
duration: number;
};
}
async function analyzeSlots(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed',
duration: number = 60000
): Promise {
const startTime = Date.now();
const startSlot = await connection.getSlot(commitment);
const progressions: number[] = [];
let previousSlot = startSlot;
while (Date.now() - startTime < duration) {
const currentSlot = await connection.getSlot(commitment);
progressions.push(currentSlot - previousSlot);
previousSlot = currentSlot;
await new Promise(resolve => setTimeout(resolve, 1000));
}
const totalProgression = progressions.reduce((sum, p) => sum + p, 0);
const durationSeconds = (Date.now() - startTime) / 1000;
return {
currentSlot: previousSlot,
slotProgression: {
average: totalProgression / progressions.length,
min: Math.min(...progressions),
max: Math.max(...progressions),
samples: progressions.length
},
timeDistribution: {
slotsPerSecond: totalProgression / durationSeconds,
slotsPerMinute: (totalProgression / durationSeconds) * 60,
slotsPerHour: (totalProgression / durationSeconds) * 3600
},
metadata: {
startTime,
duration: Date.now() - startTime
}
};
}
```
3. **Slot Synchronization**
```typescript theme={null}
interface SlotSync {
currentSlot: number;
targetSlot: number;
difference: number;
estimatedTime: number;
metadata: {
timestamp: number;
commitment: string;
};
}
class SlotSynchronizer {
private readonly targetSlotsPerSecond = 2; // Solana's target slot rate
async checkSynchronization(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed'
): Promise {
const currentSlot = await connection.getSlot(commitment);
const targetSlot = Math.floor(
(Date.now() / 1000) * this.targetSlotsPerSecond
);
return {
currentSlot,
targetSlot,
difference: currentSlot - targetSlot,
estimatedTime: Math.abs(currentSlot - targetSlot) / this.targetSlotsPerSecond,
metadata: {
timestamp: Date.now(),
commitment
}
};
}
}
```
# getSlotLeader
Source: https://docs.orbitflare.com/rpc/methods/getSlotLeader
Returns the current slot leader
## Parameters
Commitment level (processed, confirmed, finalized)
## Response
Public key of the current slot leader (base-58 encoded)
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSlotLeader",
"params": []
}'
```
### Request with Commitment
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSlotLeader",
"params": [
{
"commitment": "confirmed"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get current slot leader
const slotLeader = await connection.getSlotLeader();
console.log('Current slot leader:', slotLeader);
// Get slot leader with commitment
async function getSlotLeaderWithCommitment(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed'
) {
const slotLeader = await connection.getSlotLeader(commitment);
return {
slotLeader,
commitment,
timestamp: Date.now()
};
}
```
## Notes
1. Returns the public key of the current slot leader
2. The slot leader is responsible for producing the next block
3. Different commitment levels can be specified
4. The response is immediate as it reads from the current state
5. The slot leader changes based on the network's leader schedule
## Best Practices
1. Cache the slot leader when appropriate
2. Consider using websocket subscription for real-time updates
3. Handle network errors and retry when appropriate
4. Use appropriate commitment level based on your needs
5. Monitor slot leader changes for network analysis
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check config parameters |
| -32007 | Slot leader information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Slot Leader Monitoring**
```typescript theme={null}
interface SlotLeaderInfo {
slotLeader: string;
commitment: string;
metadata: {
timestamp: number;
previousLeader: string | null;
leaderChanged: boolean;
};
}
class SlotLeaderMonitor {
private previousLeader: string | null = null;
async monitorSlotLeader(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed',
interval: number = 1000
): Promise {
const slotLeader = await connection.getSlotLeader(commitment);
const timestamp = Date.now();
const info: SlotLeaderInfo = {
slotLeader,
commitment,
metadata: {
timestamp,
previousLeader: this.previousLeader,
leaderChanged: this.previousLeader !== null && this.previousLeader !== slotLeader
}
};
this.previousLeader = slotLeader;
return info;
}
}
```
2. **Slot Leader Analysis**
```typescript theme={null}
interface SlotLeaderAnalysis {
currentLeader: string;
leaderChanges: Array<{
from: string;
to: string;
timestamp: number;
}>;
leaderDistribution: Record;
metadata: {
startTime: number;
duration: number;
};
}
async function analyzeSlotLeaders(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed',
duration: number = 60000
): Promise {
const startTime = Date.now();
const leaderDistribution: Record = {};
const leaderChanges: Array<{
from: string;
to: string;
timestamp: number;
}> = [];
let previousLeader = await connection.getSlotLeader(commitment);
leaderDistribution[previousLeader] = 1;
while (Date.now() - startTime < duration) {
const currentLeader = await connection.getSlotLeader(commitment);
if (currentLeader !== previousLeader) {
leaderChanges.push({
from: previousLeader,
to: currentLeader,
timestamp: Date.now()
});
previousLeader = currentLeader;
}
leaderDistribution[currentLeader] = (leaderDistribution[currentLeader] || 0) + 1;
await new Promise(resolve => setTimeout(resolve, 1000));
}
return {
currentLeader: previousLeader,
leaderChanges,
leaderDistribution,
metadata: {
startTime,
duration: Date.now() - startTime
}
};
}
```
3. **Slot Leader Tracking**
```typescript theme={null}
interface SlotLeaderTrack {
leader: string;
slot: number;
metadata: {
timestamp: number;
commitment: string;
};
}
class SlotLeaderTracker {
async trackSlotLeader(
commitment: 'processed' | 'confirmed' | 'finalized' = 'confirmed'
): Promise {
const [leader, slot] = await Promise.all([
connection.getSlotLeader(commitment),
connection.getSlot()
]);
return {
leader,
slot,
metadata: {
timestamp: Date.now(),
commitment
}
};
}
}
```
# getSlotLeaders
Source: https://docs.orbitflare.com/rpc/methods/getSlotLeaders
Returns the slot leaders for a given range
## Parameters
Start slot number
Number of slot leaders to return
## Response
Array of slot leader public keys (base-58 encoded)
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSlotLeaders",
"params": [
1000000,
10
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get slot leaders
const slotLeaders = await connection.getSlotLeaders(1000000, 10);
console.log('Slot leaders:', slotLeaders);
// Get slot leaders with analysis
async function getSlotLeadersWithAnalysis(
startSlot: number,
limit: number
) {
const slotLeaders = await connection.getSlotLeaders(startSlot, limit);
return {
slotLeaders,
analysis: {
uniqueLeaders: new Set(slotLeaders).size,
leaderDistribution: slotLeaders.reduce((acc, leader) => {
acc[leader] = (acc[leader] || 0) + 1;
return acc;
}, {} as Record),
metadata: {
startSlot,
endSlot: startSlot + limit - 1,
timestamp: Date.now()
}
}
};
}
```
## Notes
1. Returns the slot leaders for a given range of slots
2. The slot leaders are responsible for producing blocks
3. The response is immediate as it reads from the current state
4. The slot leaders are determined by the network's leader schedule
5. The range must be within the current epoch
## Best Practices
1. Use appropriate start slot and limit based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in the leader schedule
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check startSlot and limit parameters |
| -32007 | Slot leader information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Leader Schedule Analysis**
```typescript theme={null}
interface LeaderScheduleAnalysis {
schedule: Array<{
slot: number;
leader: string;
}>;
distribution: Record;
metadata: {
startSlot: number;
endSlot: number;
uniqueLeaders: number;
};
}
async function analyzeLeaderSchedule(
startSlot: number,
limit: number
): Promise {
const slotLeaders = await connection.getSlotLeaders(startSlot, limit);
const schedule = slotLeaders.map((leader, index) => ({
slot: startSlot + index,
leader
}));
const distribution = slotLeaders.reduce((acc, leader) => {
acc[leader] = (acc[leader] || 0) + 1;
return acc;
}, {} as Record);
return {
schedule,
distribution,
metadata: {
startSlot,
endSlot: startSlot + limit - 1,
uniqueLeaders: Object.keys(distribution).length
}
};
}
```
2. **Leader Schedule Monitoring**
```typescript theme={null}
interface LeaderScheduleChange {
slot: number;
previousLeader: string;
currentLeader: string;
metadata: {
timestamp: number;
};
}
class LeaderScheduleMonitor {
private previousSchedule: Map = new Map();
async monitorLeaderSchedule(
startSlot: number,
limit: number,
interval: number = 60000
): Promise {
const slotLeaders = await connection.getSlotLeaders(startSlot, limit);
const changes: LeaderScheduleChange[] = [];
slotLeaders.forEach((leader, index) => {
const slot = startSlot + index;
const previousLeader = this.previousSchedule.get(slot);
if (previousLeader && previousLeader !== leader) {
changes.push({
slot,
previousLeader,
currentLeader: leader,
metadata: {
timestamp: Date.now()
}
});
}
this.previousSchedule.set(slot, leader);
});
return changes;
}
}
```
3. **Leader Schedule Planning**
```typescript theme={null}
interface LeaderSchedulePlan {
currentSlot: number;
upcomingLeaders: Array<{
slot: number;
leader: string;
estimatedTime: number;
}>;
metadata: {
timestamp: number;
slotsPerSecond: number;
};
}
class LeaderSchedulePlanner {
private readonly slotsPerSecond = 2; // Solana's target slot rate
async planLeaderSchedule(
startSlot: number,
limit: number
): Promise {
const [slotLeaders, currentSlot] = await Promise.all([
connection.getSlotLeaders(startSlot, limit),
connection.getSlot()
]);
const upcomingLeaders = slotLeaders.map((leader, index) => {
const slot = startSlot + index;
const slotsAhead = slot - currentSlot;
const estimatedTime = slotsAhead / this.slotsPerSecond;
return {
slot,
leader,
estimatedTime
};
});
return {
currentSlot,
upcomingLeaders,
metadata: {
timestamp: Date.now(),
slotsPerSecond: this.slotsPerSecond
}
};
}
}
```
# getStakeActivation
Source: https://docs.orbitflare.com/rpc/methods/getStakeActivation
Returns the stake activation information for a given account
## Parameters
Public key of the stake account (base-58 encoded)
Configuration object containing:
Commitment level (processed, confirmed, finalized)
Epoch for which to calculate stake activation
## Response
Object containing:
Activation state (active, inactive, activating, deactivating)
Active stake amount in lamports
Inactive stake amount in lamports
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getStakeActivation",
"params": [
"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj"
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get stake activation
const stakeAccount = new PublicKey('4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj');
const activation = await connection.getStakeActivation(stakeAccount);
console.log('Stake activation:', activation);
// Get stake activation with analysis
async function getStakeActivationWithAnalysis(
stakeAccount: PublicKey,
config?: { commitment?: string; epoch?: number }
) {
const activation = await connection.getStakeActivation(stakeAccount, config);
return {
activation,
analysis: {
totalStake: activation.active + activation.inactive,
activePercentage: (activation.active / (activation.active + activation.inactive)) * 100,
state: activation.state,
metadata: {
timestamp: Date.now(),
epoch: config?.epoch,
commitment: config?.commitment
}
}
};
}
```
## Notes
1. Returns the stake activation information for a given stake account
2. The activation state indicates whether the stake is active, inactive, activating, or deactivating
3. The response is immediate as it reads from the current state
4. The stake activation can change based on the epoch and commitment level
5. The stake account must be a valid stake account
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in stake activation
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check stake account public key |
| -32007 | Stake account not found | Verify the stake account exists |
| -32008 | Invalid stake account | Verify the account is a valid stake account |
## Use Cases
1. **Stake Activation Analysis**
```typescript theme={null}
interface StakeActivationAnalysis {
account: string;
activation: {
state: string;
active: number;
inactive: number;
};
metrics: {
totalStake: number;
activePercentage: number;
activationProgress?: number;
deactivationProgress?: number;
};
metadata: {
timestamp: number;
epoch?: number;
commitment?: string;
};
}
class StakeActivationAnalyzer {
async analyzeStakeActivation(
stakeAccount: PublicKey,
config?: { commitment?: string; epoch?: number }
): Promise {
const activation = await connection.getStakeActivation(stakeAccount, config);
const metrics = {
totalStake: activation.active + activation.inactive,
activePercentage: (activation.active / (activation.active + activation.inactive)) * 100
};
if (activation.state === 'activating' || activation.state === 'deactivating') {
const progress = (activation.active / (activation.active + activation.inactive)) * 100;
if (activation.state === 'activating') {
metrics.activationProgress = progress;
} else {
metrics.deactivationProgress = progress;
}
}
return {
account: stakeAccount.toBase58(),
activation,
metrics,
metadata: {
timestamp: Date.now(),
epoch: config?.epoch,
commitment: config?.commitment
}
};
}
}
```
2. **Stake Activation Monitoring**
```typescript theme={null}
interface StakeActivationChange {
account: string;
previousState: string;
currentState: string;
activeChange: number;
inactiveChange: number;
metadata: {
timestamp: number;
epoch?: number;
};
}
class StakeActivationMonitor {
private previousStates: Map = new Map();
async monitorStakeActivation(
stakeAccount: PublicKey,
config?: { commitment?: string; epoch?: number }
): Promise {
const activation = await connection.getStakeActivation(stakeAccount, config);
const accountKey = stakeAccount.toBase58();
const previous = this.previousStates.get(accountKey);
if (!previous) {
this.previousStates.set(accountKey, {
state: activation.state,
active: activation.active,
inactive: activation.inactive
});
return null;
}
if (previous.state !== activation.state ||
previous.active !== activation.active ||
previous.inactive !== activation.inactive) {
const change: StakeActivationChange = {
account: accountKey,
previousState: previous.state,
currentState: activation.state,
activeChange: activation.active - previous.active,
inactiveChange: activation.inactive - previous.inactive,
metadata: {
timestamp: Date.now(),
epoch: config?.epoch
}
};
this.previousStates.set(accountKey, {
state: activation.state,
active: activation.active,
inactive: activation.inactive
});
return change;
}
return null;
}
}
```
3. **Stake Activation Planning**
```typescript theme={null}
interface StakeActivationPlan {
account: string;
currentState: string;
targetState: string;
steps: Array<{
action: string;
estimatedTime: number;
requiredStake: number;
}>;
metadata: {
timestamp: number;
currentEpoch: number;
};
}
class StakeActivationPlanner {
private readonly slotsPerEpoch = 432000; // Solana's slots per epoch
private readonly slotsPerSecond = 2; // Solana's target slot rate
async planStakeActivation(
stakeAccount: PublicKey,
targetState: string
): Promise {
const [activation, currentEpoch] = await Promise.all([
connection.getStakeActivation(stakeAccount),
connection.getEpochInfo()
]);
const steps: Array<{
action: string;
estimatedTime: number;
requiredStake: number;
}> = [];
if (activation.state !== targetState) {
if (targetState === 'active' && activation.state === 'inactive') {
steps.push({
action: 'activate',
estimatedTime: (this.slotsPerEpoch / this.slotsPerSecond) / 2, // Half an epoch
requiredStake: activation.active + activation.inactive
});
} else if (targetState === 'inactive' && activation.state === 'active') {
steps.push({
action: 'deactivate',
estimatedTime: (this.slotsPerEpoch / this.slotsPerSecond) / 2, // Half an epoch
requiredStake: activation.active + activation.inactive
});
}
}
return {
account: stakeAccount.toBase58(),
currentState: activation.state,
targetState,
steps,
metadata: {
timestamp: Date.now(),
currentEpoch: currentEpoch.epoch
}
};
}
}
```
# getStakeMinimumDelegation
Source: https://docs.orbitflare.com/rpc/methods/getStakeMinimumDelegation
Returns the minimum stake delegation amount
## Parameters
Configuration object containing:
Commitment level (processed, confirmed, finalized)
## Response
Minimum stake delegation amount in lamports
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getStakeMinimumDelegation",
"params": []
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get minimum stake delegation
const minDelegation = await connection.getStakeMinimumDelegation();
console.log('Minimum stake delegation:', minDelegation);
// Get minimum stake delegation with analysis
async function getStakeMinimumDelegationWithAnalysis(
config?: { commitment?: string }
) {
const minDelegation = await connection.getStakeMinimumDelegation(config);
return {
minDelegation,
analysis: {
inSOL: minDelegation / 1e9, // Convert lamports to SOL
metadata: {
timestamp: Date.now(),
commitment: config?.commitment
}
}
};
}
```
## Notes
1. Returns the minimum stake delegation amount in lamports
2. The minimum delegation amount is set by the network
3. The response is immediate as it reads from the current state
4. The minimum delegation amount can change with network upgrades
5. This value is important for determining valid stake amounts
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in the minimum delegation amount
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check configuration parameters |
| -32007 | Minimum delegation information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Stake Delegation Analysis**
```typescript theme={null}
interface StakeDelegationAnalysis {
minDelegation: number;
metrics: {
inSOL: number;
recommendedMinimum: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class StakeDelegationAnalyzer {
private readonly recommendedMultiplier = 1.1; // 10% above minimum
async analyzeStakeDelegation(
config?: { commitment?: string }
): Promise {
const minDelegation = await connection.getStakeMinimumDelegation(config);
return {
minDelegation,
metrics: {
inSOL: minDelegation / 1e9,
recommendedMinimum: minDelegation * this.recommendedMultiplier
},
metadata: {
timestamp: Date.now(),
commitment: config?.commitment
}
};
}
}
```
2. **Stake Delegation Monitoring**
```typescript theme={null}
interface StakeDelegationChange {
previousMinDelegation: number;
currentMinDelegation: number;
change: number;
metadata: {
timestamp: number;
};
}
class StakeDelegationMonitor {
private previousMinDelegation: number | null = null;
async monitorStakeDelegation(
config?: { commitment?: string }
): Promise {
const currentMinDelegation = await connection.getStakeMinimumDelegation(config);
if (this.previousMinDelegation === null) {
this.previousMinDelegation = currentMinDelegation;
return null;
}
if (this.previousMinDelegation !== currentMinDelegation) {
const change: StakeDelegationChange = {
previousMinDelegation: this.previousMinDelegation,
currentMinDelegation,
change: currentMinDelegation - this.previousMinDelegation,
metadata: {
timestamp: Date.now()
}
};
this.previousMinDelegation = currentMinDelegation;
return change;
}
return null;
}
}
```
3. **Stake Delegation Planning**
```typescript theme={null}
interface StakeDelegationPlan {
minDelegation: number;
recommendations: Array<{
amount: number;
description: string;
inSOL: number;
}>;
metadata: {
timestamp: number;
};
}
class StakeDelegationPlanner {
private readonly tiers = [
{ multiplier: 1.1, description: 'Minimum recommended' },
{ multiplier: 2, description: 'Standard' },
{ multiplier: 5, description: 'Premium' }
];
async planStakeDelegation(
config?: { commitment?: string }
): Promise {
const minDelegation = await connection.getStakeMinimumDelegation(config);
const recommendations = this.tiers.map(tier => ({
amount: minDelegation * tier.multiplier,
description: tier.description,
inSOL: (minDelegation * tier.multiplier) / 1e9
}));
return {
minDelegation,
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getSupply
Source: https://docs.orbitflare.com/rpc/methods/getSupply
Returns the current supply of SOL
## Parameters
Configuration object containing:
Commitment level (processed, confirmed, finalized)
Whether to exclude non-circulating accounts from the total supply
## Response
Object containing:
Total supply in lamports
Circulating supply in lamports
Non-circulating supply in lamports
Array of non-circulating account addresses
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getSupply",
"params": []
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get supply
const supply = await connection.getSupply();
console.log('Supply:', supply);
// Get supply with analysis
async function getSupplyWithAnalysis(
config?: { commitment?: string; excludeNonCirculatingAccountsList?: boolean }
) {
const supply = await connection.getSupply(config);
return {
supply,
analysis: {
totalInSOL: supply.total / 1e9,
circulatingInSOL: supply.circulating / 1e9,
nonCirculatingInSOL: supply.nonCirculating / 1e9,
circulatingPercentage: (supply.circulating / supply.total) * 100,
metadata: {
timestamp: Date.now(),
commitment: config?.commitment
}
}
};
}
```
## Notes
1. Returns the current supply of SOL in lamports
2. The supply is broken down into total, circulating, and non-circulating amounts
3. The response is immediate as it reads from the current state
4. The supply can change with network activity and inflation
5. Non-circulating accounts are typically controlled by the Solana Foundation
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in the supply
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check configuration parameters |
| -32007 | Supply information unavailable | Node may be bootstrapping or syncing |
## Use Cases
1. **Supply Analysis**
```typescript theme={null}
interface SupplyAnalysis {
supply: {
total: number;
circulating: number;
nonCirculating: number;
};
metrics: {
totalInSOL: number;
circulatingInSOL: number;
nonCirculatingInSOL: number;
circulatingPercentage: number;
inflationRate?: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class SupplyAnalyzer {
private previousSupply: number | null = null;
async analyzeSupply(
config?: { commitment?: string; excludeNonCirculatingAccountsList?: boolean }
): Promise {
const supply = await connection.getSupply(config);
const currentTimestamp = Date.now();
const metrics = {
totalInSOL: supply.total / 1e9,
circulatingInSOL: supply.circulating / 1e9,
nonCirculatingInSOL: supply.nonCirculating / 1e9,
circulatingPercentage: (supply.circulating / supply.total) * 100
};
if (this.previousSupply !== null) {
const timeElapsed = (currentTimestamp - this.previousSupply) / (1000 * 60 * 60 * 24); // Days
const supplyChange = supply.total - this.previousSupply;
metrics.inflationRate = (supplyChange / this.previousSupply) * (365 / timeElapsed) * 100;
}
this.previousSupply = supply.total;
return {
supply,
metrics,
metadata: {
timestamp: currentTimestamp,
commitment: config?.commitment
}
};
}
}
```
2. **Supply Monitoring**
```typescript theme={null}
interface SupplyChange {
previousSupply: {
total: number;
circulating: number;
nonCirculating: number;
};
currentSupply: {
total: number;
circulating: number;
nonCirculating: number;
};
changes: {
total: number;
circulating: number;
nonCirculating: number;
};
metadata: {
timestamp: number;
};
}
class SupplyMonitor {
private previousSupply: {
total: number;
circulating: number;
nonCirculating: number;
} | null = null;
async monitorSupply(
config?: { commitment?: string; excludeNonCirculatingAccountsList?: boolean }
): Promise {
const currentSupply = await connection.getSupply(config);
if (this.previousSupply === null) {
this.previousSupply = currentSupply;
return null;
}
const changes = {
total: currentSupply.total - this.previousSupply.total,
circulating: currentSupply.circulating - this.previousSupply.circulating,
nonCirculating: currentSupply.nonCirculating - this.previousSupply.nonCirculating
};
if (changes.total !== 0 || changes.circulating !== 0 || changes.nonCirculating !== 0) {
const change: SupplyChange = {
previousSupply: this.previousSupply,
currentSupply,
changes,
metadata: {
timestamp: Date.now()
}
};
this.previousSupply = currentSupply;
return change;
}
return null;
}
}
```
3. **Supply Planning**
```typescript theme={null}
interface SupplyProjection {
currentSupply: {
total: number;
circulating: number;
nonCirculating: number;
};
projections: Array<{
days: number;
total: number;
circulating: number;
nonCirculating: number;
}>;
metadata: {
timestamp: number;
inflationRate: number;
};
}
class SupplyPlanner {
private readonly defaultInflationRate = 0.08; // 8% annual inflation
async projectSupply(
config?: { commitment?: string; excludeNonCirculatingAccountsList?: boolean },
days: number[] = [30, 90, 365]
): Promise {
const currentSupply = await connection.getSupply(config);
const currentTimestamp = Date.now();
const projections = days.map(daysAhead => {
const years = daysAhead / 365;
const total = currentSupply.total * Math.pow(1 + this.defaultInflationRate, years);
const circulating = currentSupply.circulating * Math.pow(1 + this.defaultInflationRate, years);
const nonCirculating = currentSupply.nonCirculating * Math.pow(1 + this.defaultInflationRate, years);
return {
days: daysAhead,
total,
circulating,
nonCirculating
};
});
return {
currentSupply,
projections,
metadata: {
timestamp: currentTimestamp,
inflationRate: this.defaultInflationRate
}
};
}
}
```
# getTokenAccountBalance
Source: https://docs.orbitflare.com/rpc/methods/getTokenAccountBalance
Returns the token balance of an account
## Parameters
Public key of the token account (base-58 encoded)
Configuration object containing:
Commitment level (processed, confirmed, finalized)
## Response
Object containing:
The slot the request was processed at
Raw amount of tokens as a string
Number of decimals configured for token's mint
Token amount as a float, accounting for decimals
Token amount as a string, accounting for decimals
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenAccountBalance",
"params": [
"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj"
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get token account balance
const tokenAccount = new PublicKey('4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj');
const balance = await connection.getTokenAccountBalance(tokenAccount);
console.log('Token balance:', balance);
// Get token account balance with analysis
async function getTokenAccountBalanceWithAnalysis(
tokenAccount: PublicKey,
config?: { commitment?: string }
) {
const balance = await connection.getTokenAccountBalance(tokenAccount, config);
return {
balance,
analysis: {
rawAmount: balance.value.amount,
uiAmount: balance.value.uiAmount,
uiAmountString: balance.value.uiAmountString,
decimals: balance.value.decimals,
metadata: {
timestamp: Date.now(),
commitment: config?.commitment
}
}
};
}
```
## Notes
1. Returns the token balance of a token account
2. The balance is returned in both raw and UI-friendly formats
3. The response is immediate as it reads from the current state
4. The balance can change with token transfers and other operations
5. The account must be a valid token account
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in token balances
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check token account public key |
| -32007 | Token account not found | Verify the token account exists |
| -32008 | Invalid token account | Verify the account is a valid token account |
## Use Cases
1. **Token Balance Analysis**
```typescript theme={null}
interface TokenBalanceAnalysis {
account: string;
balance: {
raw: string;
ui: number;
decimals: number;
};
metrics: {
valueInUSD?: number;
percentageChange?: number;
historicalData?: Array<{
timestamp: number;
balance: number;
}>;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class TokenBalanceAnalyzer {
private readonly tokenPrice: number = 1; // Replace with actual token price
private historicalBalances: Array<{
timestamp: number;
balance: number;
}> = [];
async analyzeTokenBalance(
tokenAccount: PublicKey,
config?: { commitment?: string }
): Promise {
const balance = await connection.getTokenAccountBalance(tokenAccount, config);
const currentTimestamp = Date.now();
this.historicalBalances.push({
timestamp: currentTimestamp,
balance: balance.value.uiAmount || 0
});
// Keep only last 24 hours of data
const oneDayAgo = currentTimestamp - 24 * 60 * 60 * 1000;
this.historicalBalances = this.historicalBalances.filter(
data => data.timestamp >= oneDayAgo
);
const metrics = {
valueInUSD: (balance.value.uiAmount || 0) * this.tokenPrice,
historicalData: this.historicalBalances
};
if (this.historicalBalances.length > 1) {
const oldestBalance = this.historicalBalances[0].balance;
const newestBalance = this.historicalBalances[this.historicalBalances.length - 1].balance;
metrics.percentageChange = ((newestBalance - oldestBalance) / oldestBalance) * 100;
}
return {
account: tokenAccount.toBase58(),
balance: {
raw: balance.value.amount,
ui: balance.value.uiAmount || 0,
decimals: balance.value.decimals
},
metrics,
metadata: {
timestamp: currentTimestamp,
commitment: config?.commitment
}
};
}
}
```
2. **Token Balance Monitoring**
```typescript theme={null}
interface TokenBalanceChange {
account: string;
previousBalance: {
raw: string;
ui: number;
};
currentBalance: {
raw: string;
ui: number;
};
change: {
raw: string;
ui: number;
};
metadata: {
timestamp: number;
};
}
class TokenBalanceMonitor {
private previousBalances: Map = new Map();
async monitorTokenBalance(
tokenAccount: PublicKey,
config?: { commitment?: string }
): Promise {
const balance = await connection.getTokenAccountBalance(tokenAccount, config);
const accountKey = tokenAccount.toBase58();
const previous = this.previousBalances.get(accountKey);
if (!previous) {
this.previousBalances.set(accountKey, {
raw: balance.value.amount,
ui: balance.value.uiAmount || 0
});
return null;
}
if (previous.raw !== balance.value.amount) {
const change: TokenBalanceChange = {
account: accountKey,
previousBalance: previous,
currentBalance: {
raw: balance.value.amount,
ui: balance.value.uiAmount || 0
},
change: {
raw: (BigInt(balance.value.amount) - BigInt(previous.raw)).toString(),
ui: (balance.value.uiAmount || 0) - previous.ui
},
metadata: {
timestamp: Date.now()
}
};
this.previousBalances.set(accountKey, {
raw: balance.value.amount,
ui: balance.value.uiAmount || 0
});
return change;
}
return null;
}
}
```
3. **Token Balance Planning**
```typescript theme={null}
interface TokenBalancePlan {
account: string;
currentBalance: {
raw: string;
ui: number;
};
projections: Array<{
days: number;
balance: number;
growth: number;
}>;
metadata: {
timestamp: number;
growthRate: number;
};
}
class TokenBalancePlanner {
private readonly defaultGrowthRate = 0.05; // 5% daily growth
async planTokenBalance(
tokenAccount: PublicKey,
config?: { commitment?: string },
days: number[] = [7, 30, 90]
): Promise {
const balance = await connection.getTokenAccountBalance(tokenAccount, config);
const currentTimestamp = Date.now();
const projections = days.map(daysAhead => {
const currentBalance = balance.value.uiAmount || 0;
const projectedBalance = currentBalance * Math.pow(1 + this.defaultGrowthRate, daysAhead);
return {
days: daysAhead,
balance: projectedBalance,
growth: projectedBalance - currentBalance
};
});
return {
account: tokenAccount.toBase58(),
currentBalance: {
raw: balance.value.amount,
ui: balance.value.uiAmount || 0
},
projections,
metadata: {
timestamp: currentTimestamp,
growthRate: this.defaultGrowthRate
}
};
}
}
```
# getTokenAccountsByDelegate
Source: https://docs.orbitflare.com/rpc/methods/getTokenAccountsByDelegate
Returns all token accounts for a given delegate
## Parameters
Public key of the delegate (base-58 encoded)
Configuration object containing:
Public key of the token program (base-58 encoded)
Commitment level (processed, confirmed, finalized)
Encoding for account data (base58, base64, jsonParsed)
Offset into account data
Length of data to return
## Response
Object containing:
The slot the request was processed at
Array of token account objects containing:
Public key of the token account (base-58 encoded)
Number of lamports in the account
Public key of the account owner (base-58 encoded)
Whether the account is executable
Epoch at which the account will next owe rent
Raw amount of tokens as a string
Number of decimals configured for token's mint
Token amount as a float, accounting for decimals
Token amount as a string, accounting for decimals
Public key of the token's mint (base-58 encoded)
Public key of the token account owner (base-58 encoded)
Public key of the delegate (base-58 encoded)
Type of account (account)
Program that owns the account (spl-token)
Number of bytes allocated to the account
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenAccountsByDelegate",
"params": [
"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj",
{
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get token accounts by delegate
const delegate = new PublicKey('4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj');
const programId = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const accounts = await connection.getTokenAccountsByDelegate(delegate, { programId });
console.log('Token accounts:', accounts);
// Get token accounts by delegate with analysis
async function getTokenAccountsByDelegateWithAnalysis(
delegate: PublicKey,
config: { programId: PublicKey; commitment?: string }
) {
const accounts = await connection.getTokenAccountsByDelegate(delegate, config);
return {
accounts,
analysis: {
totalAccounts: accounts.value.length,
totalBalance: accounts.value.reduce((sum, acc) => {
const amount = BigInt(acc.account.data.parsed.info.tokenAmount.amount);
return sum + amount;
}, BigInt(0)),
byMint: accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
acc[mint] = (acc[mint] || BigInt(0)) + amount;
return acc;
}, {} as Record),
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
```
## Notes
1. Returns all token accounts where the given public key is a delegate
2. The delegate has the authority to transfer tokens from these accounts
3. The response is immediate as it reads from the current state
4. The accounts can change with token transfers and other operations
5. The delegate must be a valid public key
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in delegated accounts
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check delegate public key and configuration |
| -32007 | Delegate not found | Verify the delegate exists |
| -32008 | Invalid program ID | Verify the program ID is correct |
## Use Cases
1. **Delegate Account Analysis**
```typescript theme={null}
interface DelegateAccountAnalysis {
delegate: string;
accounts: Array<{
pubkey: string;
mint: string;
balance: string;
owner: string;
}>;
metrics: {
totalAccounts: number;
totalBalance: string;
byMint: Record;
byOwner: Record;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class DelegateAccountAnalyzer {
async analyzeDelegateAccounts(
delegate: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise {
const accounts = await connection.getTokenAccountsByDelegate(delegate, config);
const byMint: Record = {};
const byOwner: Record = {};
let totalBalance = BigInt(0);
accounts.value.forEach(account => {
const mint = account.account.data.parsed.info.mint;
const owner = account.account.data.parsed.info.owner;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
byMint[mint] = (byMint[mint] || BigInt(0)) + amount;
byOwner[owner] = (byOwner[owner] || 0) + 1;
totalBalance += amount;
});
return {
delegate: delegate.toBase58(),
accounts: accounts.value.map(account => ({
pubkey: account.pubkey,
mint: account.account.data.parsed.info.mint,
balance: account.account.data.parsed.info.tokenAmount.amount,
owner: account.account.data.parsed.info.owner
})),
metrics: {
totalAccounts: accounts.value.length,
totalBalance: totalBalance.toString(),
byMint: Object.fromEntries(
Object.entries(byMint).map(([mint, amount]) => [mint, amount.toString()])
),
byOwner
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Delegate Account Monitoring**
```typescript theme={null}
interface DelegateAccountChange {
delegate: string;
changes: Array<{
type: 'added' | 'removed' | 'modified';
account: string;
previousBalance?: string;
currentBalance?: string;
}>;
metadata: {
timestamp: number;
};
}
class DelegateAccountMonitor {
private previousAccounts: Map = new Map();
async monitorDelegateAccounts(
delegate: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise {
const accounts = await connection.getTokenAccountsByDelegate(delegate, config);
const currentAccounts = new Map(
accounts.value.map(account => [
account.pubkey,
account.account.data.parsed.info.tokenAmount.amount
])
);
const changes: Array<{
type: 'added' | 'removed' | 'modified';
account: string;
previousBalance?: string;
currentBalance?: string;
}> = [];
// Check for removed or modified accounts
this.previousAccounts.forEach((balance, account) => {
if (!currentAccounts.has(account)) {
changes.push({
type: 'removed',
account,
previousBalance: balance
});
} else if (currentAccounts.get(account) !== balance) {
changes.push({
type: 'modified',
account,
previousBalance: balance,
currentBalance: currentAccounts.get(account)
});
}
});
// Check for added accounts
currentAccounts.forEach((balance, account) => {
if (!this.previousAccounts.has(account)) {
changes.push({
type: 'added',
account,
currentBalance: balance
});
}
});
this.previousAccounts = currentAccounts;
if (changes.length > 0) {
return {
delegate: delegate.toBase58(),
changes,
metadata: {
timestamp: Date.now()
}
};
}
return null;
}
}
```
3. **Delegate Account Planning**
```typescript theme={null}
interface DelegateAccountPlan {
delegate: string;
currentAccounts: Array<{
pubkey: string;
mint: string;
balance: string;
}>;
recommendations: Array<{
type: 'consolidate' | 'diversify' | 'rebalance';
accounts: string[];
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class DelegateAccountPlanner {
private readonly minBalancePerAccount = BigInt(1000000); // 1 token
private readonly maxAccountsPerMint = 5;
async planDelegateAccounts(
delegate: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise {
const accounts = await connection.getTokenAccountsByDelegate(delegate, config);
const byMint = accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
if (!acc[mint]) {
acc[mint] = [];
}
acc[mint].push(account);
return acc;
}, {} as Record);
const recommendations: Array<{
type: 'consolidate' | 'diversify' | 'rebalance';
accounts: string[];
reason: string;
}> = [];
Object.entries(byMint).forEach(([mint, mintAccounts]) => {
// Check for accounts with low balances
const lowBalanceAccounts = mintAccounts.filter(account =>
BigInt(account.account.data.parsed.info.tokenAmount.amount) < this.minBalancePerAccount
);
if (lowBalanceAccounts.length > 0) {
recommendations.push({
type: 'consolidate',
accounts: lowBalanceAccounts.map(account => account.pubkey),
reason: `Accounts with balances below minimum threshold for mint ${mint}`
});
}
// Check for too many accounts per mint
if (mintAccounts.length > this.maxAccountsPerMint) {
recommendations.push({
type: 'consolidate',
accounts: mintAccounts.map(account => account.pubkey),
reason: `Too many accounts (${mintAccounts.length}) for mint ${mint}`
});
}
});
return {
delegate: delegate.toBase58(),
currentAccounts: accounts.value.map(account => ({
pubkey: account.pubkey,
mint: account.account.data.parsed.info.mint,
balance: account.account.data.parsed.info.tokenAmount.amount
})),
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getTokenAccountsByOwner
Source: https://docs.orbitflare.com/rpc/methods/getTokenAccountsByOwner
Returns all token accounts for a given owner
## Parameters
Public key of the owner (base-58 encoded)
Configuration object containing:
Public key of the token program (base-58 encoded)
Commitment level (processed, confirmed, finalized)
Encoding for account data (base58, base64, jsonParsed)
Offset into account data
Length of data to return
## Response
Object containing:
The slot the request was processed at
Array of token account objects containing:
Public key of the token account (base-58 encoded)
Number of lamports in the account
Public key of the account owner (base-58 encoded)
Whether the account is executable
Epoch at which the account will next owe rent
Raw amount of tokens as a string
Number of decimals configured for token's mint
Token amount as a float, accounting for decimals
Token amount as a string, accounting for decimals
Public key of the token's mint (base-58 encoded)
Public key of the token account owner (base-58 encoded)
Public key of the delegate (base-58 encoded)
Type of account (account)
Program that owns the account (spl-token)
Number of bytes allocated to the account
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenAccountsByOwner",
"params": [
"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj",
{
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get token accounts by owner
const owner = new PublicKey('4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj');
const programId = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const accounts = await connection.getTokenAccountsByOwner(owner, { programId });
console.log('Token accounts:', accounts);
// Get token accounts by owner with analysis
async function getTokenAccountsByOwnerWithAnalysis(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
) {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
return {
accounts,
analysis: {
totalAccounts: accounts.value.length,
totalBalance: accounts.value.reduce((sum, acc) => {
const amount = BigInt(acc.account.data.parsed.info.tokenAmount.amount);
return sum + amount;
}, BigInt(0)),
byMint: accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
acc[mint] = (acc[mint] || BigInt(0)) + amount;
return acc;
}, {} as Record),
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
```
## Notes
1. Returns all token accounts owned by the given public key
2. The owner has full control over these accounts
3. The response is immediate as it reads from the current state
4. The accounts can change with token transfers and other operations
5. The owner must be a valid public key
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in owned accounts
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check owner public key and configuration |
| -32007 | Owner not found | Verify the owner exists |
| -32008 | Invalid program ID | Verify the program ID is correct |
## Use Cases
1. **Owner Account Analysis**
```typescript theme={null}
interface OwnerAccountAnalysis {
owner: string;
accounts: Array<{
pubkey: string;
mint: string;
balance: string;
delegate?: string;
}>;
metrics: {
totalAccounts: number;
totalBalance: string;
byMint: Record;
delegatedAccounts: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class OwnerAccountAnalyzer {
async analyzeOwnerAccounts(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
const byMint: Record = {};
let totalBalance = BigInt(0);
let delegatedAccounts = 0;
accounts.value.forEach(account => {
const mint = account.account.data.parsed.info.mint;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
const delegate = account.account.data.parsed.info.delegate;
byMint[mint] = (byMint[mint] || BigInt(0)) + amount;
totalBalance += amount;
if (delegate) {
delegatedAccounts++;
}
});
return {
owner: owner.toBase58(),
accounts: accounts.value.map(account => ({
pubkey: account.pubkey,
mint: account.account.data.parsed.info.mint,
balance: account.account.data.parsed.info.tokenAmount.amount,
delegate: account.account.data.parsed.info.delegate
})),
metrics: {
totalAccounts: accounts.value.length,
totalBalance: totalBalance.toString(),
byMint: Object.fromEntries(
Object.entries(byMint).map(([mint, amount]) => [mint, amount.toString()])
),
delegatedAccounts
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Owner Account Monitoring**
```typescript theme={null}
interface OwnerAccountChange {
owner: string;
changes: Array<{
type: 'added' | 'removed' | 'modified';
account: string;
previousBalance?: string;
currentBalance?: string;
previousDelegate?: string;
currentDelegate?: string;
}>;
metadata: {
timestamp: number;
};
}
class OwnerAccountMonitor {
private previousAccounts: Map = new Map();
async monitorOwnerAccounts(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
const currentAccounts = new Map(
accounts.value.map(account => [
account.pubkey,
{
balance: account.account.data.parsed.info.tokenAmount.amount,
delegate: account.account.data.parsed.info.delegate
}
])
);
const changes: Array<{
type: 'added' | 'removed' | 'modified';
account: string;
previousBalance?: string;
currentBalance?: string;
previousDelegate?: string;
currentDelegate?: string;
}> = [];
// Check for removed or modified accounts
this.previousAccounts.forEach((data, account) => {
if (!currentAccounts.has(account)) {
changes.push({
type: 'removed',
account,
previousBalance: data.balance,
previousDelegate: data.delegate
});
} else {
const current = currentAccounts.get(account)!;
if (current.balance !== data.balance || current.delegate !== data.delegate) {
changes.push({
type: 'modified',
account,
previousBalance: data.balance,
currentBalance: current.balance,
previousDelegate: data.delegate,
currentDelegate: current.delegate
});
}
}
});
// Check for added accounts
currentAccounts.forEach((data, account) => {
if (!this.previousAccounts.has(account)) {
changes.push({
type: 'added',
account,
currentBalance: data.balance,
currentDelegate: data.delegate
});
}
});
this.previousAccounts = currentAccounts;
if (changes.length > 0) {
return {
owner: owner.toBase58(),
changes,
metadata: {
timestamp: Date.now()
}
};
}
return null;
}
}
```
3. **Owner Account Planning**
```typescript theme={null}
interface OwnerAccountPlan {
owner: string;
currentAccounts: Array<{
pubkey: string;
mint: string;
balance: string;
delegate?: string;
}>;
recommendations: Array<{
type: 'consolidate' | 'diversify' | 'rebalance' | 'delegate';
accounts: string[];
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class OwnerAccountPlanner {
private readonly minBalancePerAccount = BigInt(1000000); // 1 token
private readonly maxAccountsPerMint = 5;
async planOwnerAccounts(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
const byMint = accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
if (!acc[mint]) {
acc[mint] = [];
}
acc[mint].push(account);
return acc;
}, {} as Record);
const recommendations: Array<{
type: 'consolidate' | 'diversify' | 'rebalance' | 'delegate';
accounts: string[];
reason: string;
}> = [];
Object.entries(byMint).forEach(([mint, mintAccounts]) => {
// Check for accounts with low balances
const lowBalanceAccounts = mintAccounts.filter(account =>
BigInt(account.account.data.parsed.info.tokenAmount.amount) < this.minBalancePerAccount
);
if (lowBalanceAccounts.length > 0) {
recommendations.push({
type: 'consolidate',
accounts: lowBalanceAccounts.map(account => account.pubkey),
reason: `Accounts with balances below minimum threshold for mint ${mint}`
});
}
// Check for too many accounts per mint
if (mintAccounts.length > this.maxAccountsPerMint) {
recommendations.push({
type: 'consolidate',
accounts: mintAccounts.map(account => account.pubkey),
reason: `Too many accounts (${mintAccounts.length}) for mint ${mint}`
});
}
// Check for accounts without delegates
const accountsWithoutDelegates = mintAccounts.filter(
account => !account.account.data.parsed.info.delegate
);
if (accountsWithoutDelegates.length > 0) {
recommendations.push({
type: 'delegate',
accounts: accountsWithoutDelegates.map(account => account.pubkey),
reason: `Accounts without delegates for mint ${mint}`
});
}
});
return {
owner: owner.toBase58(),
currentAccounts: accounts.value.map(account => ({
pubkey: account.pubkey,
mint: account.account.data.parsed.info.mint,
balance: account.account.data.parsed.info.tokenAmount.amount,
delegate: account.account.data.parsed.info.delegate
})),
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getTokenLargestAccounts
Source: https://docs.orbitflare.com/rpc/methods/getTokenLargestAccounts
Returns the largest token accounts for a given mint
## Parameters
Public key of the token mint (base-58 encoded)
Configuration object containing:
Maximum number of accounts to return (default: 20)
Commitment level (processed, confirmed, finalized)
## Response
Object containing:
The slot the request was processed at
Array of token account objects containing:
Public key of the token account (base-58 encoded)
Raw amount of tokens as a string
Number of decimals configured for token's mint
Token amount as a float, accounting for decimals
Token amount as a string, accounting for decimals
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenLargestAccounts",
"params": [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
{
"limit": 10
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get largest token accounts
const mint = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const accounts = await connection.getTokenLargestAccounts(mint, { limit: 10 });
console.log('Largest token accounts:', accounts);
// Get largest token accounts with analysis
async function getTokenLargestAccountsWithAnalysis(
mint: PublicKey,
config: { limit?: number; commitment?: string }
) {
const accounts = await connection.getTokenLargestAccounts(mint, config);
return {
accounts,
analysis: {
totalAccounts: accounts.value.length,
totalSupply: accounts.value.reduce((sum, acc) => {
const amount = BigInt(acc.amount);
return sum + amount;
}, BigInt(0)),
distribution: accounts.value.reduce((acc, account, index) => {
const amount = BigInt(account.amount);
const percentage = Number((amount * BigInt(100)) / BigInt(accounts.value[0].amount));
acc[account.address] = {
amount: account.amount,
percentage,
rank: index + 1
};
return acc;
}, {} as Record),
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
```
## Notes
1. Returns the largest token accounts for a given mint
2. Accounts are sorted by balance in descending order
3. The response is immediate as it reads from the current state
4. The accounts can change with token transfers and other operations
5. The mint must be a valid token mint
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in largest accounts
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check mint public key and configuration |
| -32007 | Mint not found | Verify the mint exists |
| -32008 | Invalid limit | Ensure limit is a positive number |
## Use Cases
1. **Token Distribution Analysis**
```typescript theme={null}
interface TokenDistributionAnalysis {
mint: string;
accounts: Array<{
address: string;
amount: string;
percentage: number;
rank: number;
}>;
metrics: {
totalAccounts: number;
totalSupply: string;
concentration: number; // Gini coefficient
top10Percentage: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class TokenDistributionAnalyzer {
private calculateGiniCoefficient(amounts: bigint[]): number {
const n = amounts.length;
const mean = amounts.reduce((sum, x) => sum + x, BigInt(0)) / BigInt(n);
let numerator = BigInt(0);
let denominator = BigInt(0);
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
numerator += (amounts[i] - amounts[j]).abs();
}
denominator += amounts[i];
}
return Number(numerator) / (2 * n * Number(denominator));
}
async analyzeTokenDistribution(
mint: PublicKey,
config: { limit?: number; commitment?: string }
): Promise {
const accounts = await connection.getTokenLargestAccounts(mint, config);
const amounts = accounts.value.map(acc => BigInt(acc.amount));
const totalSupply = amounts.reduce((sum, x) => sum + x, BigInt(0));
const top10Supply = amounts.slice(0, 10).reduce((sum, x) => sum + x, BigInt(0));
return {
mint: mint.toBase58(),
accounts: accounts.value.map((account, index) => ({
address: account.address,
amount: account.amount,
percentage: Number((BigInt(account.amount) * BigInt(100)) / totalSupply),
rank: index + 1
})),
metrics: {
totalAccounts: accounts.value.length,
totalSupply: totalSupply.toString(),
concentration: this.calculateGiniCoefficient(amounts),
top10Percentage: Number((top10Supply * BigInt(100)) / totalSupply)
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Token Holder Monitoring**
```typescript theme={null}
interface TokenHolderChange {
mint: string;
changes: Array<{
type: 'added' | 'removed' | 'modified';
address: string;
previousAmount?: string;
currentAmount?: string;
previousRank?: number;
currentRank?: number;
}>;
metadata: {
timestamp: number;
};
}
class TokenHolderMonitor {
private previousHolders: Map = new Map();
async monitorTokenHolders(
mint: PublicKey,
config: { limit?: number; commitment?: string }
): Promise {
const accounts = await connection.getTokenLargestAccounts(mint, config);
const currentHolders = new Map(
accounts.value.map((account, index) => [
account.address,
{
amount: account.amount,
rank: index + 1
}
])
);
const changes: Array<{
type: 'added' | 'removed' | 'modified';
address: string;
previousAmount?: string;
currentAmount?: string;
previousRank?: number;
currentRank?: number;
}> = [];
// Check for removed or modified holders
this.previousHolders.forEach((data, address) => {
if (!currentHolders.has(address)) {
changes.push({
type: 'removed',
address,
previousAmount: data.amount,
previousRank: data.rank
});
} else {
const current = currentHolders.get(address)!;
if (current.amount !== data.amount || current.rank !== data.rank) {
changes.push({
type: 'modified',
address,
previousAmount: data.amount,
currentAmount: current.amount,
previousRank: data.rank,
currentRank: current.rank
});
}
}
});
// Check for added holders
currentHolders.forEach((data, address) => {
if (!this.previousHolders.has(address)) {
changes.push({
type: 'added',
address,
currentAmount: data.amount,
currentRank: data.rank
});
}
});
this.previousHolders = currentHolders;
if (changes.length > 0) {
return {
mint: mint.toBase58(),
changes,
metadata: {
timestamp: Date.now()
}
};
}
return null;
}
}
```
3. **Token Supply Planning**
```typescript theme={null}
interface TokenSupplyPlan {
mint: string;
currentHolders: Array<{
address: string;
amount: string;
rank: number;
}>;
recommendations: Array<{
type: 'diversify' | 'consolidate' | 'rebalance';
addresses: string[];
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TokenSupplyPlanner {
private readonly maxConcentration = 0.5; // Maximum allowed concentration
private readonly minHolders = 100; // Minimum number of holders
async planTokenSupply(
mint: PublicKey,
config: { limit?: number; commitment?: string }
): Promise {
const accounts = await connection.getTokenLargestAccounts(mint, config);
const amounts = accounts.value.map(acc => BigInt(acc.amount));
const totalSupply = amounts.reduce((sum, x) => sum + x, BigInt(0));
const recommendations: Array<{
type: 'diversify' | 'consolidate' | 'rebalance';
addresses: string[];
reason: string;
}> = [];
// Check for concentration
const top10Supply = amounts.slice(0, 10).reduce((sum, x) => sum + x, BigInt(0));
const concentration = Number((top10Supply * BigInt(100)) / totalSupply) / 100;
if (concentration > this.maxConcentration) {
recommendations.push({
type: 'diversify',
addresses: accounts.value.slice(0, 10).map(acc => acc.address),
reason: `Top 10 holders control ${(concentration * 100).toFixed(2)}% of supply`
});
}
// Check for minimum holders
if (accounts.value.length < this.minHolders) {
recommendations.push({
type: 'diversify',
addresses: accounts.value.map(acc => acc.address),
reason: `Only ${accounts.value.length} holders, below minimum of ${this.minHolders}`
});
}
return {
mint: mint.toBase58(),
currentHolders: accounts.value.map((account, index) => ({
address: account.address,
amount: account.amount,
rank: index + 1
})),
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getTokenSupply
Source: https://docs.orbitflare.com/rpc/methods/getTokenSupply
Returns the total supply of a token mint
## Parameters
Public key of the token mint (base-58 encoded)
Configuration object containing:
Commitment level (processed, confirmed, finalized)
## Response
Object containing:
The slot the request was processed at
Raw amount of tokens as a string
Number of decimals configured for token's mint
Token amount as a float, accounting for decimals
Token amount as a string, accounting for decimals
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenSupply",
"params": [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get token supply
const mint = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const supply = await connection.getTokenSupply(mint);
console.log('Token supply:', supply);
// Get token supply with analysis
async function getTokenSupplyWithAnalysis(
mint: PublicKey,
config: { commitment?: string }
) {
const supply = await connection.getTokenSupply(mint, config);
return {
supply,
analysis: {
rawAmount: BigInt(supply.value.amount),
formattedAmount: supply.value.uiAmountString,
decimals: supply.value.decimals,
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
```
## Notes
1. Returns the total supply of a token mint
2. The supply includes all tokens in circulation
3. The response is immediate as it reads from the current state
4. The supply can change with token minting and burning
5. The mint must be a valid token mint
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in token supply
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check mint public key and configuration |
| -32007 | Mint not found | Verify the mint exists |
## Use Cases
1. **Token Supply Analysis**
```typescript theme={null}
interface TokenSupplyAnalysis {
mint: string;
supply: {
rawAmount: string;
formattedAmount: string;
decimals: number;
};
metrics: {
maxSupply?: string;
burnedAmount?: string;
circulatingSupply?: string;
inflationRate?: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class TokenSupplyAnalyzer {
private readonly maxSupply: Record = {
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v': BigInt(1000000000000000) // USDC
};
async analyzeTokenSupply(
mint: PublicKey,
config: { commitment?: string }
): Promise {
const supply = await connection.getTokenSupply(mint, config);
const rawAmount = BigInt(supply.value.amount);
const maxSupply = this.maxSupply[mint.toBase58()];
return {
mint: mint.toBase58(),
supply: {
rawAmount: supply.value.amount,
formattedAmount: supply.value.uiAmountString!,
decimals: supply.value.decimals
},
metrics: {
maxSupply: maxSupply?.toString(),
burnedAmount: maxSupply ? (maxSupply - rawAmount).toString() : undefined,
circulatingSupply: supply.value.uiAmountString,
inflationRate: maxSupply ? Number((rawAmount * BigInt(100)) / maxSupply) : undefined
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Token Supply Monitoring**
```typescript theme={null}
interface TokenSupplyChange {
mint: string;
changes: {
previousAmount: string;
currentAmount: string;
difference: string;
percentageChange: number;
};
metadata: {
timestamp: number;
};
}
class TokenSupplyMonitor {
private previousSupply: Map = new Map();
async monitorTokenSupply(
mint: PublicKey,
config: { commitment?: string }
): Promise {
const supply = await connection.getTokenSupply(mint, config);
const currentAmount = supply.value.amount;
const previousAmount = this.previousSupply.get(mint.toBase58());
if (previousAmount && previousAmount !== currentAmount) {
const difference = BigInt(currentAmount) - BigInt(previousAmount);
const percentageChange = Number((difference * BigInt(100)) / BigInt(previousAmount));
this.previousSupply.set(mint.toBase58(), currentAmount);
return {
mint: mint.toBase58(),
changes: {
previousAmount,
currentAmount,
difference: difference.toString(),
percentageChange
},
metadata: {
timestamp: Date.now()
}
};
}
this.previousSupply.set(mint.toBase58(), currentAmount);
return null;
}
}
```
3. **Token Supply Planning**
```typescript theme={null}
interface TokenSupplyPlan {
mint: string;
currentSupply: {
rawAmount: string;
formattedAmount: string;
decimals: number;
};
recommendations: Array<{
type: 'mint' | 'burn' | 'freeze';
amount?: string;
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TokenSupplyPlanner {
private readonly targetInflationRate = 0.02; // 2% annual inflation
private readonly maxSupply: Record = {
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v': BigInt(1000000000000000) // USDC
};
async planTokenSupply(
mint: PublicKey,
config: { commitment?: string }
): Promise {
const supply = await connection.getTokenSupply(mint, config);
const rawAmount = BigInt(supply.value.amount);
const maxSupply = this.maxSupply[mint.toBase58()];
const recommendations: Array<{
type: 'mint' | 'burn' | 'freeze';
amount?: string;
reason: string;
}> = [];
if (maxSupply) {
const currentInflationRate = Number((rawAmount * BigInt(100)) / maxSupply) / 100;
const targetAmount = maxSupply * BigInt(Math.floor(this.targetInflationRate * 100)) / BigInt(100);
if (currentInflationRate > this.targetInflationRate) {
const burnAmount = rawAmount - targetAmount;
recommendations.push({
type: 'burn',
amount: burnAmount.toString(),
reason: `Current inflation rate (${(currentInflationRate * 100).toFixed(2)}%) exceeds target (${(this.targetInflationRate * 100).toFixed(2)}%)`
});
} else if (currentInflationRate < this.targetInflationRate) {
const mintAmount = targetAmount - rawAmount;
recommendations.push({
type: 'mint',
amount: mintAmount.toString(),
reason: `Current inflation rate (${(currentInflationRate * 100).toFixed(2)}%) below target (${(this.targetInflationRate * 100).toFixed(2)}%)`
});
}
}
return {
mint: mint.toBase58(),
currentSupply: {
rawAmount: supply.value.amount,
formattedAmount: supply.value.uiAmountString!,
decimals: supply.value.decimals
},
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getTransaction
Source: https://docs.orbitflare.com/rpc/methods/getTransaction
Returns transaction details for a given signature
## Parameters
Transaction signature (base-58 encoded)
Configuration object containing:
Commitment level (processed, confirmed, finalized)
Encoding for transaction data (base58, base64, jsonParsed)
Maximum transaction version to support
## Response
Object containing:
The slot the transaction was processed in
Number of required signatures
Number of read-only signed accounts
Number of read-only unsigned accounts
Array of account public keys (base-58 encoded)
Recent blockhash used for transaction
Array of instruction objects containing:
Program ID (base-58 encoded)
Array of account indices
Instruction data (base-58 encoded)
Array of transaction signatures (base-58 encoded)
Error object if transaction failed
Fee paid for the transaction
Array of account balances before the transaction
Array of account balances after the transaction
Array of inner instruction objects
Array of log messages
Array of token balances before the transaction
Array of token balances after the transaction
Array of reward objects
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransaction",
"params": [
"5VERv8NMvzbJMEkV8xnrLkUaQwJJ9C9zV5uZ6cKf7Kq9",
{
"encoding": "jsonParsed"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, Transaction } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get transaction
const signature = '5VERv8NMvzbJMEkV8xnrLkUaQwJJ9C9zV5uZ6cKf7Kq9';
const transaction = await connection.getTransaction(signature, {
encoding: 'jsonParsed'
});
console.log('Transaction:', transaction);
// Get transaction with analysis
async function getTransactionWithAnalysis(
signature: string,
config: { commitment?: string; encoding?: string }
) {
const transaction = await connection.getTransaction(signature, config);
return {
transaction,
analysis: {
success: !transaction.meta.err,
fee: transaction.meta.fee,
balanceChanges: transaction.meta.postBalances.map((post, index) => ({
account: transaction.transaction.message.accountKeys[index],
change: post - transaction.meta.preBalances[index]
})),
tokenBalanceChanges: transaction.meta.postTokenBalances?.map((post, index) => ({
account: post.account,
mint: post.mint,
change: BigInt(post.uiTokenAmount.amount) - BigInt(transaction.meta.preTokenBalances![index].uiTokenAmount.amount)
})),
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
```
## Notes
1. Returns transaction details for a given signature
2. The transaction must be confirmed to be returned
3. The response includes detailed information about the transaction
4. The transaction can be parsed in different encodings
5. The transaction must be a valid signature
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for transaction status changes
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check signature and configuration |
| -32007 | Transaction not found | Verify the transaction exists |
| -32008 | Invalid encoding | Ensure encoding is valid |
## Use Cases
1. **Transaction Analysis**
```typescript theme={null}
interface TransactionAnalysis {
signature: string;
transaction: {
success: boolean;
fee: number;
balanceChanges: Array<{
account: string;
change: number;
}>;
tokenBalanceChanges?: Array<{
account: string;
mint: string;
change: bigint;
}>;
instructions: Array<{
programId: string;
accounts: string[];
data: string;
}>;
};
metrics: {
totalBalanceChange: number;
totalTokenBalanceChange?: bigint;
instructionCount: number;
accountCount: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class TransactionAnalyzer {
async analyzeTransaction(
signature: string,
config: { commitment?: string; encoding?: string }
): Promise {
const transaction = await connection.getTransaction(signature, config);
return {
signature,
transaction: {
success: !transaction.meta.err,
fee: transaction.meta.fee,
balanceChanges: transaction.meta.postBalances.map((post, index) => ({
account: transaction.transaction.message.accountKeys[index],
change: post - transaction.meta.preBalances[index]
})),
tokenBalanceChanges: transaction.meta.postTokenBalances?.map((post, index) => ({
account: post.account,
mint: post.mint,
change: BigInt(post.uiTokenAmount.amount) - BigInt(transaction.meta.preTokenBalances![index].uiTokenAmount.amount)
})),
instructions: transaction.transaction.message.instructions.map(ix => ({
programId: transaction.transaction.message.accountKeys[ix.programIdIndex],
accounts: ix.accounts.map(index => transaction.transaction.message.accountKeys[index]),
data: ix.data
}))
},
metrics: {
totalBalanceChange: transaction.meta.postBalances.reduce((sum, post, index) =>
sum + (post - transaction.meta.preBalances[index]), 0),
totalTokenBalanceChange: transaction.meta.postTokenBalances?.reduce((sum, post, index) =>
sum + (BigInt(post.uiTokenAmount.amount) - BigInt(transaction.meta.preTokenBalances![index].uiTokenAmount.amount)), BigInt(0)),
instructionCount: transaction.transaction.message.instructions.length,
accountCount: transaction.transaction.message.accountKeys.length
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Transaction Monitoring**
```typescript theme={null}
interface TransactionChange {
signature: string;
changes: {
status: 'confirmed' | 'failed' | 'dropped';
previousStatus?: 'confirmed' | 'failed' | 'dropped';
error?: any;
};
metadata: {
timestamp: number;
};
}
class TransactionMonitor {
private previousTransactions: Map = new Map();
async monitorTransaction(
signature: string,
config: { commitment?: string; encoding?: string }
): Promise {
const transaction = await connection.getTransaction(signature, config);
const currentStatus = transaction ? (transaction.meta.err ? 'failed' : 'confirmed') : 'dropped';
const previous = this.previousTransactions.get(signature);
if (previous && (previous.status !== currentStatus || previous.error !== transaction?.meta.err)) {
this.previousTransactions.set(signature, {
status: currentStatus,
error: transaction?.meta.err
});
return {
signature,
changes: {
status: currentStatus,
previousStatus: previous.status,
error: transaction?.meta.err
},
metadata: {
timestamp: Date.now()
}
};
}
this.previousTransactions.set(signature, {
status: currentStatus,
error: transaction?.meta.err
});
return null;
}
}
```
3. **Transaction Planning**
```typescript theme={null}
interface TransactionPlan {
signature: string;
transaction: {
success: boolean;
fee: number;
balanceChanges: Array<{
account: string;
change: number;
}>;
tokenBalanceChanges?: Array<{
account: string;
mint: string;
change: bigint;
}>;
};
recommendations: Array<{
type: 'optimize' | 'retry' | 'cancel';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TransactionPlanner {
private readonly maxFee = 1000000; // 0.001 SOL
private readonly minBalance = 1000000; // 0.001 SOL
async planTransaction(
signature: string,
config: { commitment?: string; encoding?: string }
): Promise {
const transaction = await connection.getTransaction(signature, config);
const recommendations: Array<{
type: 'optimize' | 'retry' | 'cancel';
reason: string;
}> = [];
// Check for high fees
if (transaction.meta.fee > this.maxFee) {
recommendations.push({
type: 'optimize',
reason: `Transaction fee (${transaction.meta.fee}) exceeds maximum (${this.maxFee})`
});
}
// Check for low balances
const lowBalanceAccounts = transaction.meta.postBalances
.map((post, index) => ({
account: transaction.transaction.message.accountKeys[index],
balance: post
}))
.filter(acc => acc.balance < this.minBalance);
if (lowBalanceAccounts.length > 0) {
recommendations.push({
type: 'optimize',
reason: `Accounts with low balances: ${lowBalanceAccounts.map(acc => acc.account).join(', ')}`
});
}
// Check for failed transaction
if (transaction.meta.err) {
recommendations.push({
type: 'retry',
reason: `Transaction failed with error: ${JSON.stringify(transaction.meta.err)}`
});
}
return {
signature,
transaction: {
success: !transaction.meta.err,
fee: transaction.meta.fee,
balanceChanges: transaction.meta.postBalances.map((post, index) => ({
account: transaction.transaction.message.accountKeys[index],
change: post - transaction.meta.preBalances[index]
})),
tokenBalanceChanges: transaction.meta.postTokenBalances?.map((post, index) => ({
account: post.account,
mint: post.mint,
change: BigInt(post.uiTokenAmount.amount) - BigInt(transaction.meta.preTokenBalances![index].uiTokenAmount.amount)
}))
},
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getTransactionCount
Source: https://docs.orbitflare.com/rpc/methods/getTransactionCount
Returns the current transaction count from the ledger
## Parameters
Configuration object containing:
Commitment level (processed, confirmed, finalized)
## Response
The current transaction count
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTransactionCount"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get transaction count
const count = await connection.getTransactionCount();
console.log('Transaction count:', count);
// Get transaction count with analysis
async function getTransactionCountWithAnalysis(
config: { commitment?: string }
) {
const count = await connection.getTransactionCount(config);
return {
count,
analysis: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
```
## Notes
1. Returns the current transaction count from the ledger
2. The count includes all transactions processed by the node
3. The response is immediate as it reads from the current state
4. The count can change with new transactions
5. The count may vary between nodes due to network propagation
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in transaction count
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check configuration parameters |
## Use Cases
1. **Transaction Count Analysis**
```typescript theme={null}
interface TransactionCountAnalysis {
count: number;
metrics: {
timestamp: number;
commitment?: string;
};
}
class TransactionCountAnalyzer {
async analyzeTransactionCount(
config: { commitment?: string }
): Promise {
const count = await connection.getTransactionCount(config);
return {
count,
metrics: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Transaction Count Monitoring**
```typescript theme={null}
interface TransactionCountChange {
changes: {
previousCount: number;
currentCount: number;
difference: number;
percentageChange: number;
};
metadata: {
timestamp: number;
};
}
class TransactionCountMonitor {
private previousCount: number | null = null;
async monitorTransactionCount(
config: { commitment?: string }
): Promise {
const currentCount = await connection.getTransactionCount(config);
if (this.previousCount !== null && this.previousCount !== currentCount) {
const difference = currentCount - this.previousCount;
const percentageChange = (difference / this.previousCount) * 100;
this.previousCount = currentCount;
return {
changes: {
previousCount: this.previousCount,
currentCount,
difference,
percentageChange
},
metadata: {
timestamp: Date.now()
}
};
}
this.previousCount = currentCount;
return null;
}
}
```
3. **Transaction Count Planning**
```typescript theme={null}
interface TransactionCountPlan {
currentCount: number;
recommendations: Array<{
type: 'scale' | 'optimize' | 'monitor';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TransactionCountPlanner {
private readonly maxTransactionsPerSecond = 1000;
private readonly minTransactionsPerSecond = 100;
async planTransactionCount(
config: { commitment?: string }
): Promise {
const currentCount = await connection.getTransactionCount(config);
const recommendations: Array<{
type: 'scale' | 'optimize' | 'monitor';
reason: string;
}> = [];
// Check for high transaction rate
if (currentCount > this.maxTransactionsPerSecond) {
recommendations.push({
type: 'scale',
reason: `Transaction count (${currentCount}) exceeds maximum (${this.maxTransactionsPerSecond})`
});
}
// Check for low transaction rate
if (currentCount < this.minTransactionsPerSecond) {
recommendations.push({
type: 'optimize',
reason: `Transaction count (${currentCount}) below minimum (${this.minTransactionsPerSecond})`
});
}
return {
currentCount,
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# getVersion
Source: https://docs.orbitflare.com/rpc/methods/getVersion
Returns the current version of the node
## Parameters
None
## Response
Object containing:
Version of the Solana core software
Feature set version
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getVersion"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get version
const version = await connection.getVersion();
console.log('Node version:', version);
// Get version with analysis
async function getVersionWithAnalysis() {
const version = await connection.getVersion();
return {
version,
analysis: {
timestamp: Date.now(),
isLatest: await checkLatestVersion(version['solana-core'])
}
};
}
async function checkLatestVersion(currentVersion: string): Promise {
// Implementation to check if current version is latest
// This would typically involve calling an external API
return true;
}
```
## Notes
1. Returns the current version of the node
2. The version includes the Solana core software version
3. The response is immediate as it reads from the current state
4. The version can change with node updates
5. Different nodes may run different versions
## Best Practices
1. Cache results when appropriate to reduce RPC load
2. Monitor for version changes
3. Consider using websocket subscription for real-time updates
4. Handle network errors and retry when appropriate
5. Keep track of version compatibility
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
## Use Cases
1. **Version Analysis**
```typescript theme={null}
interface VersionAnalysis {
version: {
core: string;
featureSet?: number;
};
metrics: {
timestamp: number;
isLatest: boolean;
isCompatible: boolean;
};
}
class VersionAnalyzer {
private readonly minCompatibleVersion = '1.8.0';
async analyzeVersion(): Promise {
const version = await connection.getVersion();
return {
version: {
core: version['solana-core'],
featureSet: version['feature-set']
},
metrics: {
timestamp: Date.now(),
isLatest: await this.checkLatestVersion(version['solana-core']),
isCompatible: this.checkCompatibility(version['solana-core'])
}
};
}
private async checkLatestVersion(currentVersion: string): Promise {
// Implementation to check if current version is latest
return true;
}
private checkCompatibility(currentVersion: string): boolean {
const [currentMajor, currentMinor] = currentVersion.split('.').map(Number);
const [minMajor, minMinor] = this.minCompatibleVersion.split('.').map(Number);
return currentMajor > minMajor ||
(currentMajor === minMajor && currentMinor >= minMinor);
}
}
```
2. **Version Monitoring**
```typescript theme={null}
interface VersionChange {
changes: {
previousVersion: string;
currentVersion: string;
type: 'upgrade' | 'downgrade';
};
metadata: {
timestamp: number;
};
}
class VersionMonitor {
private previousVersion: string | null = null;
async monitorVersion(): Promise {
const version = await connection.getVersion();
const currentVersion = version['solana-core'];
if (this.previousVersion !== null && this.previousVersion !== currentVersion) {
const type = this.compareVersions(currentVersion, this.previousVersion) > 0 ? 'upgrade' : 'downgrade';
this.previousVersion = currentVersion;
return {
changes: {
previousVersion: this.previousVersion,
currentVersion,
type
},
metadata: {
timestamp: Date.now()
}
};
}
this.previousVersion = currentVersion;
return null;
}
private compareVersions(v1: string, v2: string): number {
const [major1, minor1, patch1] = v1.split('.').map(Number);
const [major2, minor2, patch2] = v2.split('.').map(Number);
if (major1 !== major2) return major1 - major2;
if (minor1 !== minor2) return minor1 - minor2;
return patch1 - patch2;
}
}
```
3. **Version Planning**
```typescript theme={null}
interface VersionPlan {
currentVersion: string;
recommendations: Array<{
type: 'upgrade' | 'downgrade' | 'maintain';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class VersionPlanner {
private readonly targetVersion = '1.9.0';
private readonly minCompatibleVersion = '1.8.0';
async planVersion(): Promise {
const version = await connection.getVersion();
const currentVersion = version['solana-core'];
const recommendations: Array<{
type: 'upgrade' | 'downgrade' | 'maintain';
reason: string;
}> = [];
// Check for version compatibility
if (!this.checkCompatibility(currentVersion)) {
recommendations.push({
type: 'upgrade',
reason: `Current version (${currentVersion}) is not compatible with minimum version (${this.minCompatibleVersion})`
});
}
// Check for target version
if (this.compareVersions(currentVersion, this.targetVersion) < 0) {
recommendations.push({
type: 'upgrade',
reason: `Current version (${currentVersion}) is below target version (${this.targetVersion})`
});
}
return {
currentVersion,
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
private checkCompatibility(currentVersion: string): boolean {
const [currentMajor, currentMinor] = currentVersion.split('.').map(Number);
const [minMajor, minMinor] = this.minCompatibleVersion.split('.').map(Number);
return currentMajor > minMajor ||
(currentMajor === minMajor && currentMinor >= minMinor);
}
private compareVersions(v1: string, v2: string): number {
const [major1, minor1, patch1] = v1.split('.').map(Number);
const [major2, minor2, patch2] = v2.split('.').map(Number);
if (major1 !== major2) return major1 - major2;
if (minor1 !== minor2) return minor1 - minor2;
return patch1 - patch2;
}
}
```
# getVoteAccounts
Source: https://docs.orbitflare.com/rpc/methods/getVoteAccounts
Returns the current vote accounts
## Parameters
Configuration object containing:
Commitment level (processed, confirmed, finalized)
Public key of a specific vote account to query (base-58 encoded)
Whether to include unstaked delinquent vote accounts
Maximum slot distance to consider a vote account delinquent
## Response
Object containing:
Array of current vote account objects containing:
Public key of the vote account (base-58 encoded)
Public key of the validator (base-58 encoded)
Amount of activated stake in lamports
Whether the account voted in the current epoch
Array of epoch credit objects containing:
Epoch number
Number of credits earned
Number of credits earned in previous epoch
Slot of the last vote
Slot of the root
Array of delinquent vote account objects with the same structure as current
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getVoteAccounts"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get vote accounts
const voteAccounts = await connection.getVoteAccounts();
console.log('Vote accounts:', voteAccounts);
// Get vote accounts with analysis
async function getVoteAccountsWithAnalysis(
config: {
commitment?: string;
votePubkey?: string;
keepUnstakedDelinquents?: boolean;
delinquentSlotDistance?: number;
}
) {
const voteAccounts = await connection.getVoteAccounts(config);
return {
voteAccounts,
analysis: {
current: {
count: voteAccounts.current.length,
totalStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0),
averageStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.current.length,
activeVoters: voteAccounts.current.filter(acc => acc.epochVoteAccount).length
},
delinquent: {
count: voteAccounts.delinquent.length,
totalStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0),
averageStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.delinquent.length,
activeVoters: voteAccounts.delinquent.filter(acc => acc.epochVoteAccount).length
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
```
## Notes
1. Returns the current vote accounts
2. The response includes both current and delinquent vote accounts
3. The response is immediate as it reads from the current state
4. The accounts can change with stake changes and voting
5. The vote accounts must be valid
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in vote accounts
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check configuration parameters |
| -32007 | Vote account not found | Verify the vote account exists |
## Use Cases
1. **Vote Account Analysis**
```typescript theme={null}
interface VoteAccountAnalysis {
accounts: {
current: Array<{
votePubkey: string;
nodePubkey: string;
activatedStake: number;
epochVoteAccount: boolean;
lastVote: number;
rootSlot?: number;
}>;
delinquent: Array<{
votePubkey: string;
nodePubkey: string;
activatedStake: number;
epochVoteAccount: boolean;
lastVote: number;
rootSlot?: number;
}>;
};
metrics: {
current: {
count: number;
totalStake: number;
averageStake: number;
activeVoters: number;
participationRate: number;
};
delinquent: {
count: number;
totalStake: number;
averageStake: number;
activeVoters: number;
participationRate: number;
};
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class VoteAccountAnalyzer {
async analyzeVoteAccounts(
config: {
commitment?: string;
votePubkey?: string;
keepUnstakedDelinquents?: boolean;
delinquentSlotDistance?: number;
}
): Promise {
const voteAccounts = await connection.getVoteAccounts(config);
return {
accounts: {
current: voteAccounts.current.map(acc => ({
votePubkey: acc.votePubkey,
nodePubkey: acc.nodePubkey,
activatedStake: acc.activatedStake,
epochVoteAccount: acc.epochVoteAccount,
lastVote: acc.lastVote,
rootSlot: acc.rootSlot
})),
delinquent: voteAccounts.delinquent.map(acc => ({
votePubkey: acc.votePubkey,
nodePubkey: acc.nodePubkey,
activatedStake: acc.activatedStake,
epochVoteAccount: acc.epochVoteAccount,
lastVote: acc.lastVote,
rootSlot: acc.rootSlot
}))
},
metrics: {
current: {
count: voteAccounts.current.length,
totalStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0),
averageStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.current.length,
activeVoters: voteAccounts.current.filter(acc => acc.epochVoteAccount).length,
participationRate: voteAccounts.current.filter(acc => acc.epochVoteAccount).length / voteAccounts.current.length
},
delinquent: {
count: voteAccounts.delinquent.length,
totalStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0),
averageStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.delinquent.length,
activeVoters: voteAccounts.delinquent.filter(acc => acc.epochVoteAccount).length,
participationRate: voteAccounts.delinquent.filter(acc => acc.epochVoteAccount).length / voteAccounts.delinquent.length
}
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
```
2. **Vote Account Monitoring**
```typescript theme={null}
interface VoteAccountChange {
changes: {
current: Array<{
type: 'added' | 'removed' | 'modified';
votePubkey: string;
previousStake?: number;
currentStake?: number;
previousVote?: boolean;
currentVote?: boolean;
}>;
delinquent: Array<{
type: 'added' | 'removed' | 'modified';
votePubkey: string;
previousStake?: number;
currentStake?: number;
previousVote?: boolean;
currentVote?: boolean;
}>;
};
metadata: {
timestamp: number;
};
}
class VoteAccountMonitor {
private previousAccounts: {
current: Map;
delinquent: Map;
} = {
current: new Map(),
delinquent: new Map()
};
async monitorVoteAccounts(
config: {
commitment?: string;
votePubkey?: string;
keepUnstakedDelinquents?: boolean;
delinquentSlotDistance?: number;
}
): Promise {
const voteAccounts = await connection.getVoteAccounts(config);
const changes: {
current: Array<{
type: 'added' | 'removed' | 'modified';
votePubkey: string;
previousStake?: number;
currentStake?: number;
previousVote?: boolean;
currentVote?: boolean;
}>;
delinquent: Array<{
type: 'added' | 'removed' | 'modified';
votePubkey: string;
previousStake?: number;
currentStake?: number;
previousVote?: boolean;
currentVote?: boolean;
}>;
} = {
current: [],
delinquent: []
};
// Check current accounts
this.checkAccountChanges(
voteAccounts.current,
this.previousAccounts.current,
changes.current
);
// Check delinquent accounts
this.checkAccountChanges(
voteAccounts.delinquent,
this.previousAccounts.delinquent,
changes.delinquent
);
// Update previous accounts
this.previousAccounts.current = new Map(
voteAccounts.current.map(acc => [
acc.votePubkey,
{
stake: acc.activatedStake,
vote: acc.epochVoteAccount
}
])
);
this.previousAccounts.delinquent = new Map(
voteAccounts.delinquent.map(acc => [
acc.votePubkey,
{
stake: acc.activatedStake,
vote: acc.epochVoteAccount
}
])
);
if (changes.current.length > 0 || changes.delinquent.length > 0) {
return {
changes,
metadata: {
timestamp: Date.now()
}
};
}
return null;
}
private checkAccountChanges(
currentAccounts: Array<{
votePubkey: string;
activatedStake: number;
epochVoteAccount: boolean;
}>,
previousAccounts: Map,
changes: Array<{
type: 'added' | 'removed' | 'modified';
votePubkey: string;
previousStake?: number;
currentStake?: number;
previousVote?: boolean;
currentVote?: boolean;
}>
) {
// Check for removed or modified accounts
previousAccounts.forEach((data, votePubkey) => {
const current = currentAccounts.find(acc => acc.votePubkey === votePubkey);
if (!current) {
changes.push({
type: 'removed',
votePubkey,
previousStake: data.stake,
previousVote: data.vote
});
} else if (
current.activatedStake !== data.stake ||
current.epochVoteAccount !== data.vote
) {
changes.push({
type: 'modified',
votePubkey,
previousStake: data.stake,
currentStake: current.activatedStake,
previousVote: data.vote,
currentVote: current.epochVoteAccount
});
}
});
// Check for added accounts
currentAccounts.forEach(account => {
if (!previousAccounts.has(account.votePubkey)) {
changes.push({
type: 'added',
votePubkey: account.votePubkey,
currentStake: account.activatedStake,
currentVote: account.epochVoteAccount
});
}
});
}
}
```
3. **Vote Account Planning**
```typescript theme={null}
interface VoteAccountPlan {
accounts: {
current: Array<{
votePubkey: string;
nodePubkey: string;
activatedStake: number;
epochVoteAccount: boolean;
}>;
delinquent: Array<{
votePubkey: string;
nodePubkey: string;
activatedStake: number;
epochVoteAccount: boolean;
}>;
};
recommendations: Array<{
type: 'stake' | 'unstake' | 'monitor';
votePubkey: string;
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class VoteAccountPlanner {
private readonly minStake = 1000000000; // 1 SOL
private readonly maxDelinquentVotes = 3;
async planVoteAccounts(
config: {
commitment?: string;
votePubkey?: string;
keepUnstakedDelinquents?: boolean;
delinquentSlotDistance?: number;
}
): Promise {
const voteAccounts = await connection.getVoteAccounts(config);
const recommendations: Array<{
type: 'stake' | 'unstake' | 'monitor';
votePubkey: string;
reason: string;
}> = [];
// Check current accounts
voteAccounts.current.forEach(account => {
if (account.activatedStake < this.minStake) {
recommendations.push({
type: 'stake',
votePubkey: account.votePubkey,
reason: `Account has low stake (${account.activatedStake})`
});
}
if (!account.epochVoteAccount) {
recommendations.push({
type: 'monitor',
votePubkey: account.votePubkey,
reason: 'Account not voting in current epoch'
});
}
});
// Check delinquent accounts
voteAccounts.delinquent.forEach(account => {
if (account.activatedStake > 0) {
recommendations.push({
type: 'unstake',
votePubkey: account.votePubkey,
reason: 'Account is delinquent with stake'
});
}
});
return {
accounts: {
current: voteAccounts.current.map(acc => ({
votePubkey: acc.votePubkey,
nodePubkey: acc.nodePubkey,
activatedStake: acc.activatedStake,
epochVoteAccount: acc.epochVoteAccount
})),
delinquent: voteAccounts.delinquent.map(acc => ({
votePubkey: acc.votePubkey,
nodePubkey: acc.nodePubkey,
activatedStake: acc.activatedStake,
epochVoteAccount: acc.epochVoteAccount
}))
},
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
# isBlockhashValid
Source: https://docs.orbitflare.com/rpc/methods/isBlockhashValid
Returns whether a blockhash is still valid or not
## Parameters
The blockhash to validate (base-58 encoded)
Configuration object containing:
Commitment level (processed, confirmed, finalized)
The minimum slot that the request can be evaluated at
## Response
Whether the blockhash is still valid (true) or not (false)
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "isBlockhashValid",
"params": [
"J7rBdM6AecPDEZp8aPq5tPmsPzPhQG4HD6YtAcQBDfJj"
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Check if blockhash is valid
const isValid = await connection.isBlockhashValid('J7rBdM6AecPDEZp8aPq5tPmsPzPhQG4HD6YtAcQBDfJj');
console.log('Is blockhash valid:', isValid);
// Check blockhash validity with analysis
async function checkBlockhashValidityWithAnalysis(
blockhash: string,
config: {
commitment?: string;
minContextSlot?: number;
}
) {
const isValid = await connection.isBlockhashValid(blockhash, config);
return {
blockhash,
validity: {
isValid,
checkedAt: Date.now()
},
config: {
commitment: config.commitment,
minContextSlot: config.minContextSlot
}
};
}
```
## Notes
1. Returns whether a blockhash is still valid
2. Blockhashes expire after a certain number of slots
3. The response is immediate as it reads from the current state
4. Invalid blockhashes cannot be used in transactions
5. The blockhash must be base-58 encoded
## Best Practices
1. Use appropriate commitment level based on your needs
2. Cache results when appropriate to reduce RPC load
3. Check blockhash validity before sending transactions
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check the blockhash format |
| -32007 | Blockhash not found | The blockhash has expired or is invalid |
## Use Cases
1. **Blockhash Validation**
```typescript theme={null}
interface BlockhashValidation {
blockhash: string;
validity: {
isValid: boolean;
checkedAt: number;
};
config: {
commitment?: string;
minContextSlot?: number;
};
}
class BlockhashValidator {
async validateBlockhash(
blockhash: string,
config: {
commitment?: string;
minContextSlot?: number;
}
): Promise {
const isValid = await connection.isBlockhashValid(blockhash, config);
return {
blockhash,
validity: {
isValid,
checkedAt: Date.now()
},
config: {
commitment: config.commitment,
minContextSlot: config.minContextSlot
}
};
}
}
```
2. **Blockhash Monitoring**
```typescript theme={null}
interface BlockhashStatus {
blockhash: string;
status: {
isValid: boolean;
lastChecked: number;
checkCount: number;
};
history: Array<{
timestamp: number;
isValid: boolean;
}>;
}
class BlockhashMonitor {
private statuses: Map = new Map();
async monitorBlockhash(
blockhash: string,
config: {
commitment?: string;
minContextSlot?: number;
}
): Promise {
const isValid = await connection.isBlockhashValid(blockhash, config);
const now = Date.now();
let status = this.statuses.get(blockhash);
if (!status) {
status = {
blockhash,
status: {
isValid,
lastChecked: now,
checkCount: 1
},
history: [{
timestamp: now,
isValid
}]
};
} else {
status.status.isValid = isValid;
status.status.lastChecked = now;
status.status.checkCount++;
status.history.push({
timestamp: now,
isValid
});
}
this.statuses.set(blockhash, status);
return status;
}
}
```
3. **Transaction Planning**
```typescript theme={null}
interface TransactionPlan {
blockhash: string;
validity: {
isValid: boolean;
checkedAt: number;
};
recommendations: Array<{
type: 'proceed' | 'retry' | 'abort';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TransactionPlanner {
private readonly maxRetries = 3;
private readonly validityCheckInterval = 1000; // 1 second
async planTransaction(
blockhash: string,
config: {
commitment?: string;
minContextSlot?: number;
}
): Promise {
const isValid = await connection.isBlockhashValid(blockhash, config);
const now = Date.now();
const recommendations: Array<{
type: 'proceed' | 'retry' | 'abort';
reason: string;
}> = [];
if (isValid) {
recommendations.push({
type: 'proceed',
reason: 'Blockhash is valid'
});
} else {
recommendations.push({
type: 'abort',
reason: 'Blockhash is invalid'
});
recommendations.push({
type: 'retry',
reason: 'Get a new blockhash and retry'
});
}
return {
blockhash,
validity: {
isValid,
checkedAt: now
},
recommendations,
metadata: {
timestamp: now
}
};
}
async waitForValidBlockhash(
blockhash: string,
config: {
commitment?: string;
minContextSlot?: number;
}
): Promise {
let retries = 0;
while (retries < this.maxRetries) {
const isValid = await connection.isBlockhashValid(blockhash, config);
if (isValid) {
return true;
}
retries++;
await new Promise(resolve => setTimeout(resolve, this.validityCheckInterval));
}
return false;
}
}
```
# minimumLedgerSlot
Source: https://docs.orbitflare.com/rpc/methods/minimumLedgerSlot
Returns the lowest slot that the node has information about in its ledger
## Parameters
This method does not accept any parameters.
## Response
The lowest slot that the node has information about in its ledger
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "minimumLedgerSlot"
}'
```
### Using web3.js
```typescript theme={null}
import { Connection } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get minimum ledger slot
const minSlot = await connection.minimumLedgerSlot();
console.log('Minimum ledger slot:', minSlot);
// Get minimum ledger slot with analysis
async function getMinimumLedgerSlotWithAnalysis() {
const minSlot = await connection.minimumLedgerSlot();
const currentSlot = await connection.getSlot();
return {
slots: {
minimum: minSlot,
current: currentSlot,
difference: currentSlot - minSlot
},
metadata: {
timestamp: Date.now()
}
};
}
```
## Notes
1. Returns the lowest slot in the node's ledger
2. The response is immediate as it reads from the current state
3. The minimum slot can change as the ledger is pruned
4. Slots below this value are not accessible
5. The value may vary between nodes based on their configuration
## Best Practices
1. Use this method to determine ledger boundaries
2. Cache results when appropriate to reduce RPC load
3. Monitor for changes in minimum slot
4. Consider using websocket subscription for real-time updates
5. Handle network errors and retry when appropriate
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32603 | Internal error | Node may be bootstrapping or syncing |
## Use Cases
1. **Ledger Analysis**
```typescript theme={null}
interface LedgerAnalysis {
slots: {
minimum: number;
current: number;
difference: number;
};
metrics: {
pruningRate: number;
retentionPeriod: number;
};
metadata: {
timestamp: number;
};
}
class LedgerAnalyzer {
private readonly slotsPerSecond = 2; // Solana's target slots per second
async analyzeLedger(): Promise {
const minSlot = await connection.minimumLedgerSlot();
const currentSlot = await connection.getSlot();
const difference = currentSlot - minSlot;
return {
slots: {
minimum: minSlot,
current: currentSlot,
difference
},
metrics: {
pruningRate: difference / this.slotsPerSecond, // seconds
retentionPeriod: difference / (this.slotsPerSecond * 3600) // hours
},
metadata: {
timestamp: Date.now()
}
};
}
}
```
2. **Ledger Monitoring**
```typescript theme={null}
interface LedgerStatus {
slots: {
minimum: number;
previous: number;
difference: number;
};
history: Array<{
timestamp: number;
minimum: number;
}>;
}
class LedgerMonitor {
private previousMinSlot: number | null = null;
private history: Array<{
timestamp: number;
minimum: number;
}> = [];
async monitorLedger(): Promise {
const minSlot = await connection.minimumLedgerSlot();
const now = Date.now();
this.history.push({
timestamp: now,
minimum: minSlot
});
if (this.previousMinSlot === null) {
this.previousMinSlot = minSlot;
return null;
}
const status: LedgerStatus = {
slots: {
minimum: minSlot,
previous: this.previousMinSlot,
difference: minSlot - this.previousMinSlot
},
history: this.history
};
this.previousMinSlot = minSlot;
return status;
}
}
```
3. **Ledger Planning**
```typescript theme={null}
interface LedgerPlan {
slots: {
minimum: number;
current: number;
difference: number;
};
recommendations: Array<{
type: 'fetch' | 'wait' | 'error';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class LedgerPlanner {
private readonly maxSlotRange = 1000000; // Maximum slots to search
async planLedgerOperation(
targetSlot: number
): Promise {
const minSlot = await connection.minimumLedgerSlot();
const currentSlot = await connection.getSlot();
const now = Date.now();
const recommendations: Array<{
type: 'fetch' | 'wait' | 'error';
reason: string;
}> = [];
if (targetSlot < minSlot) {
recommendations.push({
type: 'error',
reason: `Target slot ${targetSlot} is below minimum ledger slot ${minSlot}`
});
} else if (targetSlot > currentSlot) {
recommendations.push({
type: 'wait',
reason: `Target slot ${targetSlot} has not been reached yet`
});
} else if (currentSlot - targetSlot > this.maxSlotRange) {
recommendations.push({
type: 'error',
reason: `Target slot ${targetSlot} is too far in the past`
});
} else {
recommendations.push({
type: 'fetch',
reason: 'Target slot is within valid range'
});
}
return {
slots: {
minimum: minSlot,
current: currentSlot,
difference: currentSlot - minSlot
},
recommendations,
metadata: {
timestamp: now
}
};
}
}
```
# requestAirdrop
Source: https://docs.orbitflare.com/rpc/methods/requestAirdrop
Requests an airdrop of lamports to a Solana account
## Parameters
Public key of the account to receive the airdrop (base-58 encoded)
Amount of lamports to request (1 SOL = 1,000,000,000 lamports)
Configuration object containing:
Commitment level (processed, confirmed, finalized)
## Response
Transaction signature of the airdrop (base-58 encoded)
## Code Examples
### Basic Request
```bash theme={null}
curl https://devnet.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "requestAirdrop",
"params": [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",
1000000000
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://devnet.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Request airdrop
const signature = await connection.requestAirdrop(
new PublicKey('83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri'),
1000000000 // 1 SOL
);
console.log('Airdrop signature:', signature);
// Request airdrop with confirmation
async function requestAirdropWithConfirmation(
pubkey: string,
lamports: number,
config: { commitment?: string }
) {
const signature = await connection.requestAirdrop(
new PublicKey(pubkey),
lamports,
config
);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature);
return {
signature,
confirmation: {
status: confirmation.value.err ? 'failed' : 'success',
slot: confirmation.context.slot
},
request: {
pubkey,
lamports,
formatted: lamports / 1e9 + ' SOL'
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
```
## Notes
1. Requests an airdrop of SOL to a Solana account
2. Only available on test networks (devnet, testnet)
3. The response is the transaction signature
4. The transaction must be confirmed before funds are available
5. The amount is limited based on network configuration
## Best Practices
1. Use appropriate commitment level based on your needs
2. Wait for transaction confirmation before proceeding
3. Handle rate limits and network errors
4. Consider using faucet services for larger amounts
5. Monitor transaction status for failures
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a test network |
| -32602 | Invalid params | Check public key format and lamports amount |
| -32003 | Transaction simulation failed | Amount may exceed limit or account may be invalid |
## Use Cases
1. **Airdrop Management**
```typescript theme={null}
interface AirdropRequest {
request: {
pubkey: string;
lamports: number;
formatted: string;
};
transaction: {
signature: string;
status: 'pending' | 'success' | 'failed';
confirmations: number;
};
metadata: {
timestamp: number;
network: string;
commitment?: string;
};
}
class AirdropManager {
private readonly maxRetries = 3;
private readonly confirmationTimeout = 30000; // 30 seconds
async requestAirdrop(
pubkey: string,
lamports: number,
config: { commitment?: string }
): Promise {
let retries = 0;
let signature: string;
while (retries < this.maxRetries) {
try {
signature = await connection.requestAirdrop(
new PublicKey(pubkey),
lamports,
config
);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, config.commitment);
if (!confirmation.value.err) {
return {
request: {
pubkey,
lamports,
formatted: lamports / 1e9 + ' SOL'
},
transaction: {
signature,
status: 'success',
confirmations: 1
},
metadata: {
timestamp: Date.now(),
network: connection.rpcEndpoint,
commitment: config.commitment
}
};
}
} catch (error) {
retries++;
if (retries === this.maxRetries) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
}
```
2. **Airdrop Monitoring**
```typescript theme={null}
interface AirdropStatus {
request: {
pubkey: string;
lamports: number;
};
status: {
signature: string;
confirmations: number;
slot: number;
};
history: Array<{
timestamp: number;
status: string;
confirmations: number;
}>;
}
class AirdropMonitor {
private requests: Map = new Map();
async monitorAirdrop(
signature: string,
request: {
pubkey: string;
lamports: number;
}
): Promise {
let status = this.requests.get(signature);
const now = Date.now();
const confirmation = await connection.getSignatureStatus(signature);
if (!status) {
status = {
request,
status: {
signature,
confirmations: confirmation?.confirmations || 0,
slot: confirmation?.slot || 0
},
history: [{
timestamp: now,
status: confirmation ? 'confirmed' : 'pending',
confirmations: confirmation?.confirmations || 0
}]
};
} else {
status.status.confirmations = confirmation?.confirmations || 0;
status.status.slot = confirmation?.slot || 0;
status.history.push({
timestamp: now,
status: confirmation ? 'confirmed' : 'pending',
confirmations: confirmation?.confirmations || 0
});
}
this.requests.set(signature, status);
return status;
}
}
```
3. **Airdrop Planning**
```typescript theme={null}
interface AirdropPlan {
request: {
pubkey: string;
lamports: number;
formatted: string;
};
recommendations: Array<{
type: 'proceed' | 'split' | 'abort';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class AirdropPlanner {
private readonly maxAirdrop = 2000000000; // 2 SOL
private readonly minAirdrop = 100000; // 0.0001 SOL
async planAirdrop(
pubkey: string,
lamports: number
): Promise {
const recommendations: Array<{
type: 'proceed' | 'split' | 'abort';
reason: string;
}> = [];
if (lamports > this.maxAirdrop) {
recommendations.push({
type: 'split',
reason: `Amount ${lamports} exceeds maximum airdrop of ${this.maxAirdrop}`
});
} else if (lamports < this.minAirdrop) {
recommendations.push({
type: 'abort',
reason: `Amount ${lamports} is below minimum airdrop of ${this.minAirdrop}`
});
} else {
recommendations.push({
type: 'proceed',
reason: 'Amount is within valid range'
});
}
return {
request: {
pubkey,
lamports,
formatted: lamports / 1e9 + ' SOL'
},
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
async splitAirdrop(
pubkey: string,
totalLamports: number
): Promise> {
const chunks: Array<{
lamports: number;
delay: number;
}> = [];
let remaining = totalLamports;
let delay = 0;
while (remaining > 0) {
const chunk = Math.min(remaining, this.maxAirdrop);
chunks.push({
lamports: chunk,
delay
});
remaining -= chunk;
delay += 2000; // 2 seconds between requests
}
return chunks;
}
}
```
# sendTransaction
Source: https://docs.orbitflare.com/rpc/methods/sendTransaction
Submits a signed transaction to the cluster for processing
## Parameters
Fully-signed transaction as a base-64 encoded string
Configuration object containing:
Encoding format for the transaction (base58, base64, json)
Commitment level (processed, confirmed, finalized)
Commitment level for preflight (processed, confirmed, finalized)
If true, skip the preflight transaction checks
Maximum number of times for the RPC node to retry sending the transaction to the leader
The minimum slot that the request can be evaluated at
## Response
First transaction signature embedded in the transaction (base-58 encoded)
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "sendTransaction",
"params": [
"4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTFNhyKNMr3WNFCrXgDS7uV7u",
{
"encoding": "base64",
"commitment": "confirmed"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, Transaction, PublicKey, SystemProgram } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Create and send transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey('83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri'),
toPubkey: new PublicKey('J7rBdM6AecPDEZp8aPq5tPmsPzPhQG4HD6YtAcQBDfJj'),
lamports: 1000000000
})
);
const signature = await connection.sendTransaction(transaction, [signer]);
console.log('Transaction signature:', signature);
// Send transaction with confirmation
async function sendTransactionWithConfirmation(
transaction: Transaction,
signers: Array,
config: {
commitment?: string;
preflightCommitment?: string;
skipPreflight?: boolean;
maxRetries?: number;
}
) {
const signature = await connection.sendTransaction(
transaction,
signers,
config
);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, config.commitment);
return {
signature,
confirmation: {
status: confirmation.value.err ? 'failed' : 'success',
slot: confirmation.context.slot
},
transaction: {
recentBlockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
```
## Notes
1. Submits a signed transaction to the cluster
2. The transaction must be fully signed
3. The response is the transaction signature
4. The transaction must be confirmed before changes are applied
5. The transaction must include a recent blockhash
## Best Practices
1. Use appropriate commitment level based on your needs
2. Wait for transaction confirmation before proceeding
3. Handle rate limits and network errors
4. Monitor transaction status for failures
5. Consider using preflight checks for validation
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check transaction format and encoding |
| -32003 | Transaction simulation failed | Verify transaction validity and account balances |
| -32004 | Blockhash not found | Use a more recent blockhash |
| -32005 | Node is behind | Try a different RPC node |
| -32006 | Transaction already processed | Transaction was already submitted |
| -32007 | Transaction signature verification failed | Check transaction signatures |
## Use Cases
1. **Transaction Management**
```typescript theme={null}
interface TransactionRequest {
transaction: {
signature: string;
blockhash: string;
feePayer: string;
instructions: number;
};
status: {
state: 'pending' | 'success' | 'failed';
confirmations: number;
slot?: number;
error?: string;
};
metadata: {
timestamp: number;
commitment?: string;
retries: number;
};
}
class TransactionManager {
private readonly maxRetries = 3;
private readonly confirmationTimeout = 30000; // 30 seconds
async sendTransaction(
transaction: Transaction,
signers: Array,
config: {
commitment?: string;
preflightCommitment?: string;
skipPreflight?: boolean;
}
): Promise {
let retries = 0;
let signature: string;
while (retries < this.maxRetries) {
try {
// Get a new blockhash if retrying
if (retries > 0) {
const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash;
}
signature = await connection.sendTransaction(
transaction,
signers,
config
);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, config.commitment);
if (!confirmation.value.err) {
return {
transaction: {
signature,
blockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
status: {
state: 'success',
confirmations: 1,
slot: confirmation.context.slot
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment,
retries
}
};
}
throw new Error(confirmation.value.err.toString());
} catch (error) {
retries++;
if (retries === this.maxRetries) {
return {
transaction: {
signature,
blockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
status: {
state: 'failed',
confirmations: 0,
error: error.message
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment,
retries
}
};
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
}
```
2. **Transaction Monitoring**
```typescript theme={null}
interface TransactionStatus {
transaction: {
signature: string;
slot?: number;
};
status: {
confirmations: number;
err: any;
};
history: Array<{
timestamp: number;
confirmations: number;
error?: string;
}>;
}
class TransactionMonitor {
private transactions: Map = new Map();
async monitorTransaction(
signature: string
): Promise {
let status = this.transactions.get(signature);
const now = Date.now();
const confirmation = await connection.getSignatureStatus(signature);
if (!status) {
status = {
transaction: {
signature,
slot: confirmation?.slot
},
status: {
confirmations: confirmation?.confirmations || 0,
err: confirmation?.err
},
history: [{
timestamp: now,
confirmations: confirmation?.confirmations || 0,
error: confirmation?.err?.toString()
}]
};
} else {
status.status.confirmations = confirmation?.confirmations || 0;
status.status.err = confirmation?.err;
status.history.push({
timestamp: now,
confirmations: confirmation?.confirmations || 0,
error: confirmation?.err?.toString()
});
}
this.transactions.set(signature, status);
return status;
}
}
```
3. **Transaction Planning**
```typescript theme={null}
interface TransactionPlan {
transaction: {
instructions: number;
signers: number;
estimatedFee: number;
};
recommendations: Array<{
type: 'proceed' | 'split' | 'abort';
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class TransactionPlanner {
private readonly maxInstructions = 10;
private readonly maxSigners = 8;
async planTransaction(
transaction: Transaction
): Promise {
const { feeCalculator } = await connection.getRecentBlockhash();
const estimatedFee = feeCalculator.lamportsPerSignature * transaction.signatures.length;
const recommendations: Array<{
type: 'proceed' | 'split' | 'abort';
reason: string;
}> = [];
if (transaction.instructions.length > this.maxInstructions) {
recommendations.push({
type: 'split',
reason: `Transaction has ${transaction.instructions.length} instructions, exceeding maximum of ${this.maxInstructions}`
});
}
if (transaction.signatures.length > this.maxSigners) {
recommendations.push({
type: 'split',
reason: `Transaction has ${transaction.signatures.length} signers, exceeding maximum of ${this.maxSigners}`
});
}
if (recommendations.length === 0) {
recommendations.push({
type: 'proceed',
reason: 'Transaction is within limits'
});
}
return {
transaction: {
instructions: transaction.instructions.length,
signers: transaction.signatures.length,
estimatedFee
},
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
async splitTransaction(
transaction: Transaction
): Promise;
}>> {
const chunks: Array<{
transaction: Transaction;
signers: Array;
}> = [];
let currentChunk = new Transaction();
let currentSigners: Array = [];
for (let i = 0; i < transaction.instructions.length; i++) {
const instruction = transaction.instructions[i];
if (currentChunk.instructions.length >= this.maxInstructions) {
chunks.push({
transaction: currentChunk,
signers: currentSigners
});
currentChunk = new Transaction();
currentSigners = [];
}
currentChunk.add(instruction);
// Add unique signers
instruction.keys
.filter(key => key.isSigner)
.forEach(key => {
if (!currentSigners.find(signer => signer.equals(key.pubkey))) {
currentSigners.push(key.pubkey);
}
});
}
if (currentChunk.instructions.length > 0) {
chunks.push({
transaction: currentChunk,
signers: currentSigners
});
}
return chunks;
}
}
```
# simulateTransaction
Source: https://docs.orbitflare.com/rpc/methods/simulateTransaction
Simulates sending a transaction
## Parameters
Transaction to simulate, as a base-64 encoded string
Configuration object containing:
If true, verify transaction signatures
Commitment level (processed, confirmed, finalized)
Encoding format for the transaction (base58, base64, json)
If true, replace the transaction's recent blockhash with the most recent blockhash
Accounts configuration object containing:
Encoding format for account data (base58, base64, json)
Array of account addresses to return data for
The minimum slot that the request can be evaluated at
## Response
Object containing:
Error if transaction simulation failed, null if simulation succeeded
Array of log messages the transaction instructions output during execution
Array of accounts with their data (if requested)
Number of compute units consumed by the transaction
Program return data from the simulation
## Code Examples
### Basic Request
```bash theme={null}
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "simulateTransaction",
"params": [
"4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTFNhyKNMr3WNFCrXgDS7uV7u",
{
"sigVerify": false,
"commitment": "confirmed",
"encoding": "base64"
}
]
}'
```
### Using web3.js
```typescript theme={null}
import { Connection, Transaction, PublicKey, SystemProgram } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Create and simulate transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey('83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri'),
toPubkey: new PublicKey('J7rBdM6AecPDEZp8aPq5tPmsPzPhQG4HD6YtAcQBDfJj'),
lamports: 1000000000
})
);
const simulation = await connection.simulateTransaction(transaction);
console.log('Simulation result:', simulation);
// Simulate transaction with analysis
async function simulateTransactionWithAnalysis(
transaction: Transaction,
config: {
sigVerify?: boolean;
commitment?: string;
replaceRecentBlockhash?: boolean;
}
) {
const simulation = await connection.simulateTransaction(transaction, config);
return {
simulation: {
success: !simulation.value.err,
error: simulation.value.err,
logs: simulation.value.logs,
unitsConsumed: simulation.value.unitsConsumed
},
transaction: {
recentBlockhash: transaction.recentBlockhash,
feePayer: transaction.feePayer?.toBase58(),
instructions: transaction.instructions.length
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
```
## Notes
1. Simulates sending a transaction without actually sending it
2. The response includes execution logs and errors
3. The simulation is performed on the current state
4. No state changes are persisted
5. Useful for testing and debugging transactions
## Best Practices
1. Use simulation before sending real transactions
2. Check compute unit consumption
3. Analyze program logs for errors
4. Verify account states after simulation
5. Handle simulation errors appropriately
## Common Errors
| Code | Message | Solution |
| - | - | - |
| -32601 | Method not found | Verify you're connected to a Solana RPC node |
| -32602 | Invalid params | Check transaction format and encoding |
| -32003 | Transaction simulation failed | Verify transaction validity and account balances |
| -32004 | Blockhash not found | Use a more recent blockhash |
| -32005 | Node is behind | Try a different RPC node |
## Use Cases
1. **Transaction Analysis**
```typescript theme={null}
interface TransactionAnalysis {
transaction: {
instructions: number;
signers: number;
estimatedUnits: number;
};
simulation: {
success: boolean;
error: string | null;
logs: string[];
unitsConsumed: number;
};
analysis: {
programInvocations: number;
accountAccesses: number;
errorAnalysis?: string;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class TransactionAnalyzer {
async analyzeTransaction(
transaction: Transaction,
config: {
sigVerify?: boolean;
commitment?: string;
}
): Promise {
const simulation = await connection.simulateTransaction(transaction, config);
const logs = simulation.value.logs || [];
const programInvocations = logs.filter(log =>
log.includes('Program') && log.includes('invoke')
).length;
const accountAccesses = logs.filter(log =>
log.includes('Account') || log.includes('Token')
).length;
let errorAnalysis;
if (simulation.value.err) {
errorAnalysis = this.analyzeError(simulation.value.err);
}
return {
transaction: {
instructions: transaction.instructions.length,
signers: transaction.signatures.length,
estimatedUnits: simulation.value.unitsConsumed || 0
},
simulation: {
success: !simulation.value.err,
error: simulation.value.err,
logs,
unitsConsumed: simulation.value.unitsConsumed || 0
},
analysis: {
programInvocations,
accountAccesses,
errorAnalysis
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
private analyzeError(error: any): string {
if (typeof error === 'string') {
if (error.includes('insufficient funds')) {
return 'Transaction failed due to insufficient funds';
}
if (error.includes('invalid account owner')) {
return 'Transaction failed due to invalid account ownership';
}
if (error.includes('account not found')) {
return 'Transaction failed due to missing account';
}
}
return 'Unknown error occurred during simulation';
}
}
```
2. **Transaction Validation**
```typescript theme={null}
interface TransactionValidation {
transaction: {
signature: string;
blockhash: string;
feePayer: string;
};
checks: Array<{
name: string;
passed: boolean;
details?: string;
}>;
recommendations: Array<{
type: 'fix' | 'warning' | 'info';
message: string;
}>;
metadata: {
timestamp: number;
};
}
class TransactionValidator {
private readonly maxUnits = 200000;
private readonly minAccountRent = 890880;
async validateTransaction(
transaction: Transaction,
config: {
sigVerify?: boolean;
commitment?: string;
}
): Promise {
const simulation = await connection.simulateTransaction(transaction, config);
const checks: Array<{
name: string;
passed: boolean;
details?: string;
}> = [];
const recommendations: Array<{
type: 'fix' | 'warning' | 'info';
message: string;
}> = [];
// Check compute units
const unitsConsumed = simulation.value.unitsConsumed || 0;
checks.push({
name: 'Compute Units',
passed: unitsConsumed <= this.maxUnits,
details: `${unitsConsumed} units consumed`
});
if (unitsConsumed > this.maxUnits) {
recommendations.push({
type: 'fix',
message: 'Transaction exceeds compute unit limit'
});
}
// Check for errors
checks.push({
name: 'Simulation',
passed: !simulation.value.err,
details: simulation.value.err?.toString()
});
if (simulation.value.err) {
recommendations.push({
type: 'fix',
message: `Fix simulation error: ${simulation.value.err}`
});
}
// Analyze logs
const logs = simulation.value.logs || [];
checks.push({
name: 'Program Execution',
passed: logs.some(log => log.includes('Program log:')),
details: 'Program execution logs present'
});
// Check account rent
const rentWarnings = logs.filter(log =>
log.includes('insufficient lamports for rent')
);
if (rentWarnings.length > 0) {
checks.push({
name: 'Account Rent',
passed: false,
details: 'Insufficient lamports for rent'
});
recommendations.push({
type: 'fix',
message: `Ensure accounts have minimum rent: ${this.minAccountRent} lamports`
});
}
return {
transaction: {
signature: transaction.signatures[0]?.signature?.toString() || '',
blockhash: transaction.recentBlockhash || '',
feePayer: transaction.feePayer?.toBase58() || ''
},
checks,
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}
```
3. **Transaction Optimization**
```typescript theme={null}
interface TransactionOptimization {
original: {
instructions: number;
unitsConsumed: number;
};
optimized: {
instructions: number;
unitsConsumed: number;
savings: number;
};
changes: Array<{
type: 'merge' | 'remove' | 'reorder';
description: string;
impact: number;
}>;
metadata: {
timestamp: number;
};
}
class TransactionOptimizer {
async optimizeTransaction(
transaction: Transaction
): Promise {
// Simulate original transaction
const originalSimulation = await connection.simulateTransaction(transaction);
const originalUnits = originalSimulation.value.unitsConsumed || 0;
const changes: Array<{
type: 'merge' | 'remove' | 'reorder';
description: string;
impact: number;
}> = [];
// Create optimized transaction
const optimizedTransaction = new Transaction();
const instructions = [...transaction.instructions];
// Analyze and merge similar instructions
const mergedInstructions = this.mergeInstructions(instructions);
if (mergedInstructions.length < instructions.length) {
changes.push({
type: 'merge',
description: `Merged ${instructions.length - mergedInstructions.length} similar instructions`,
impact: 0 // Will be updated after simulation
});
}
// Remove redundant instructions
const cleanedInstructions = this.removeRedundantInstructions(mergedInstructions);
if (cleanedInstructions.length < mergedInstructions.length) {
changes.push({
type: 'remove',
description: `Removed ${mergedInstructions.length - cleanedInstructions.length} redundant instructions`,
impact: 0
});
}
// Reorder instructions for optimal execution
const orderedInstructions = this.reorderInstructions(cleanedInstructions);
changes.push({
type: 'reorder',
description: 'Reordered instructions for optimal execution',
impact: 0
});
// Add optimized instructions
orderedInstructions.forEach(instruction => {
optimizedTransaction.add(instruction);
});
// Simulate optimized transaction
const optimizedSimulation = await connection.simulateTransaction(optimizedTransaction);
const optimizedUnits = optimizedSimulation.value.unitsConsumed || 0;
// Calculate impacts
const totalSavings = originalUnits - optimizedUnits;
changes.forEach(change => {
change.impact = Math.floor(totalSavings / changes.length);
});
return {
original: {
instructions: instructions.length,
unitsConsumed: originalUnits
},
optimized: {
instructions: orderedInstructions.length,
unitsConsumed: optimizedUnits,
savings: totalSavings
},
changes,
metadata: {
timestamp: Date.now()
}
};
}
private mergeInstructions(
instructions: Array
): Array {
// Implementation would analyze and merge similar instructions
// This is a placeholder that returns the original instructions
return instructions;
}
private removeRedundantInstructions(
instructions: Array
): Array {
// Implementation would identify and remove redundant instructions
// This is a placeholder that returns the original instructions
return instructions;
}
private reorderInstructions(
instructions: Array
): Array {
// Implementation would reorder instructions for optimal execution
// This is a placeholder that returns the original instructions
return instructions;
}
}
```
# WebSocket API
Source: https://docs.orbitflare.com/rpc/websocket
Real-time Solana subscriptions for accounts, slots, logs, and more
## Overview
OrbitFlare supports persistent WebSocket connections for real-time Solana data. WebSocket subscriptions push updates to your client as they occur on-chain — no polling required.
WebSocket endpoints use the `wss://` scheme with the same region codes as the HTTP API:
```
wss://{region}.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY
```
For a region endpoint (Frankfurt shown):
```
wss://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY
```
Your license key is found in the **Licenses** section of your [OrbitFlare Dashboard](https://orbitflare.com/dashboard). Pass it as the `api_key` query parameter — the same key used for HTTP RPC.
## Connecting
```javascript theme={null}
const WebSocket = require("ws");
const ws = new WebSocket(
"wss://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY"
);
ws.on("open", () => {
console.log("Connected");
});
ws.on("message", (data) => {
const msg = JSON.parse(data);
console.log("Received:", msg);
});
ws.on("error", (err) => {
console.error("WebSocket error:", err);
});
ws.on("close", (code, reason) => {
console.log(`Connection closed: ${code} ${reason}`);
});
```
WebSocket connections idle for more than **60 seconds** without any message exchange are closed by the server. Send a ping or keep your subscriptions active to maintain the connection.
## Subscription Methods
### accountSubscribe
Receive updates whenever a specific account's lamport balance or data changes.
```javascript theme={null}
ws.on("open", () => {
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "accountSubscribe",
params: [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",
{ encoding: "base64", commitment: "confirmed" }
],
}));
});
```
**Update fields:**
| Field | Description |
| - | - |
| `lamports` | Current lamport balance |
| `data` | Account data (encoding determined by the `encoding` param) |
| `owner` | Owning program public key |
| `executable` | Whether the account is an executable program |
| `rentEpoch` | Epoch at which rent is next owed |
***
### logsSubscribe
Receive transaction log messages in real time. Optionally filter to a specific program.
```javascript theme={null}
// All transactions
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 2,
method: "logsSubscribe",
params: ["all"],
}));
// Filter to a specific program
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 3,
method: "logsSubscribe",
params: [
{ mentions: ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"] }, // Raydium AMM
{ commitment: "confirmed" }
],
}));
```
**Update fields:**
| Field | Description |
| - | - |
| `signature` | Transaction signature (base-58) |
| `err` | `null` if successful, error object if failed |
| `logs` | Array of log message strings |
***
### slotSubscribe
Receive a notification each time a slot is processed.
```javascript theme={null}
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 4,
method: "slotSubscribe",
params: [],
}));
```
**Update fields:**
| Field | Description |
| - | - |
| `slot` | Current slot number |
| `parent` | Parent slot number |
| `root` | Highest confirmed root slot |
***
### signatureSubscribe
Receive a one-time notification when a specific transaction signature reaches a given commitment level.
```javascript theme={null}
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 5,
method: "signatureSubscribe",
params: [
"TRANSACTION_SIGNATURE_HERE",
{ commitment: "finalized" }
],
}));
```
The subscription automatically closes after the first notification is sent.
***
### programSubscribe
Receive updates for all accounts owned by a specific program.
```javascript theme={null}
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 6,
method: "programSubscribe",
params: [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", // SPL Token
{
encoding: "jsonParsed",
commitment: "confirmed",
filters: [{ dataSize: 165 }], // Standard token account size
}
],
}));
```
***
### blockSubscribe
Receive full block data as each block is confirmed. **Requires an add-on — contact support to enable.**
```javascript theme={null}
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 7,
method: "blockSubscribe",
params: ["all", { commitment: "confirmed", encoding: "base64" }],
}));
```
***
### rootSubscribe
Receive the highest confirmed root slot each time it advances.
```javascript theme={null}
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 8,
method: "rootSubscribe",
params: [],
}));
```
***
### voteSubscribe
Receive raw vote transactions as they are observed in the gossip network. **High volume — filter carefully.**
```javascript theme={null}
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 9,
method: "voteSubscribe",
params: [],
}));
```
## Unsubscribing
Each subscription method has a corresponding `Unsubscribe` method. Pass the subscription ID returned in the initial subscription response.
```javascript theme={null}
// Subscribe and capture the subscription ID
ws.on("message", (data) => {
const msg = JSON.parse(data);
// Subscription confirmation contains the subscription ID
if (msg.id === 1 && msg.result !== undefined) {
const subscriptionId = msg.result;
console.log("Subscribed, ID:", subscriptionId);
// Unsubscribe later
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 10,
method: "accountUnsubscribe",
params: [subscriptionId],
}));
}
});
```
Available unsubscribe methods: `accountUnsubscribe`, `logsUnsubscribe`, `slotUnsubscribe`, `signatureUnsubscribe`, `programUnsubscribe`, `blockUnsubscribe`, `rootUnsubscribe`, `voteUnsubscribe`.
## Keepalive
WebSocket connections are closed after 60 seconds of inactivity. Send a JSON-RPC ping periodically to keep the connection alive:
```javascript theme={null}
const pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ jsonrpc: "2.0", id: 0, method: "ping" }));
}
}, 30_000);
ws.on("close", () => clearInterval(pingInterval));
```
## Reconnection
Implement automatic reconnection with exponential backoff for production applications:
```javascript theme={null}
function connect(url, onMessage) {
let ws;
let delay = 1000;
let attempt = 0;
function createConnection() {
ws = new WebSocket(url);
ws.on("open", () => {
console.log("Connected (attempt", attempt + 1, ")");
delay = 1000; // reset backoff on successful connection
attempt = 0;
// re-subscribe to all channels here
});
ws.on("message", onMessage);
ws.on("close", () => {
attempt++;
const nextDelay = Math.min(delay * 2, 30_000);
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(createConnection, delay);
delay = nextDelay;
});
ws.on("error", (err) => console.error("WebSocket error:", err));
}
createConnection();
return () => ws?.close();
}
const disconnect = connect(
"wss://fra.rpc.orbitflare.com?api_key=YOUR_LICENSE_KEY",
(data) => {
const msg = JSON.parse(data);
console.log("Update:", msg);
}
);
```
## Connection Limits
WebSocket connections share the same per-IP limit as gRPC connections. See [Authentication & Limits](/authentication#websocket) for details.
## See Also
JSON-RPC 2.0 request format, batch requests, and all available methods.
HTTP status codes, JSON-RPC errors, and how to handle them.
# EVM SDK
Source: https://docs.orbitflare.com/sdk/evm
Rust SDK for OrbitFlare's EVM chains, the orbitflare-evm-sdk crate.
One Rust client for OrbitFlare's EVM chains - Polygon, BNB Smart Chain, and Robinhood Chain - plus any EVM chain you define yourself. Built on [alloy](https://github.com/alloy-rs/alloy) types throughout (`Address`, `U256`, `B256`, `Filter`, `TransactionRequest`, typed blocks, transactions, receipts, and logs), with OrbitFlare's own transport: endpoint failover, retry with backoff, and self-healing WebSocket subscriptions.
The clients are generic over the chain (`RpcClient`, `WsClient`); the aliases below are convenience shorthands. gRPC is currently Polygon only (Bor).
## Supported chains
| Chain | Chain ID | Alias |
| - | - | - |
| Polygon | 137 | `PolygonRpcClient`, `PolygonWsClient` |
| BNB Smart Chain | 56 | `BnbRpcClient`, `BnbWsClient` |
| Robinhood Chain | 4663 | `RobinhoodRpcClient`, `RobinhoodWsClient` |
| Your own | any | `RpcClient` where `impl Chain for C` |
Migrating from `orbitflare-robinhood-sdk`? Robinhood Chain is now covered here via `RobinhoodRpcClient` / `RobinhoodWsClient`. The RPC and WebSocket surface is the same; the standalone crate is deprecated.
## Install
```bash theme={null}
cargo add orbitflare-evm-sdk
```
Only the RPC client is enabled by default. Enable what you need:
```bash theme={null}
cargo add orbitflare-evm-sdk --features ws
cargo add orbitflare-evm-sdk --features grpc
cargo add orbitflare-evm-sdk --features all
```
## RPC
```rust Example theme={null}
let client = PolygonRpcClient::builder()
.url("https://ams.poly.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()?;
let block = client.get_block_number().await?;
let gas_price = client.get_gas_price().await?;
```
```rust Full Example theme={null}
use orbitflare_evm_sdk::{primitives::address, PolygonRpcClient, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = PolygonRpcClient::builder()
.url("https://ams.poly.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()?;
let block = client.get_block_number().await?;
let gas_price = client.get_gas_price().await?;
let wallet = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
let balance = client.get_balance(wallet).await?;
println!("block {block}, gas {gas_price} wei, balance {balance} wei");
Ok(())
}
```
There are no baked-in default endpoints: set the URL with `.url()` or via an environment variable (see [Endpoints](#endpoints)), and the API key with `.api_key()` or `ORBITFLARE_LICENSE_KEY`.
### Builder methods
**`.url(url)`** - The primary endpoint. Resolution order: `.url()` on the builder, then the `ORBITFLARE_RPC_URL` environment variable.
**`.urls(&[...])`** - Set the primary and all fallbacks in one call. The first element is the primary, the rest are fallbacks.
**`.fallback_url(url)` / `.fallback_urls(&[...])`** - Add failover endpoints. Failing endpoints are quarantined with exponential cooldown (10s, 20s, 40s, max 60s) and automatically retried once the cooldown expires; healthy endpoints are always preferred.
**`.api_key(key)`** - Your OrbitFlare license key. If not set, the SDK checks `ORBITFLARE_LICENSE_KEY`. The key is appended to the endpoint URL as `?api_key=` at request time.
**`.block_tag(tag)`** - Default block tag used by state queries (`get_balance`, `call`, `get_code`, ...). Defaults to `Latest`.
**`.retry(policy)`** - Controls retry on transient errors (5xx, 429, connection resets, JSON-RPC error code -32005) with exponential backoff before failing over to the next endpoint.
**`.timeout(duration)`** - HTTP timeout for each individual request.
### Available methods
| Method | Returns |
| - | - |
| `get_block_number()` | `u64` |
| `get_chain_id()` | `u64` |
| `get_balance(Address)` | `U256` wei |
| `get_transaction_count(Address)` | `u64` nonce |
| `get_gas_price()` | `u128` wei |
| `max_priority_fee_per_gas()` | `u128` wei |
| `get_block_by_number(impl Into, full_txs)` | `Option` |
| `get_block_by_hash(B256, full_txs)` | `Option` |
| `get_transaction_by_hash(B256)` | `Option` |
| `get_transaction_receipt(B256)` | `Option` |
| `get_logs(&Filter)` | `Vec` |
| `get_code(Address)` | `Bytes` |
| `get_storage_at(Address, B256)` | `B256` |
| `call(&TransactionRequest)` | `Bytes` |
| `estimate_gas(&TransactionRequest)` | `u64` |
| `send_raw_transaction(&[u8])` | `B256` tx hash |
| `fee_history(blocks, newest, percentiles)` | `FeeHistory` |
| `request(method, params)` | Any RPC method by name (`serde_json::Value`) |
| `request_raw(body)` | Raw JSON-RPC body string |
### Log filters
`get_logs` takes alloy's `Filter` directly:
```rust Example theme={null}
let filter = Filter::new()
.from_block(60_000_000u64)
.to_block(BlockNumberOrTag::Latest)
.address(address!("0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"))
.event_signature(transfer_topic);
let logs = client.get_logs(&filter).await?;
```
```rust Full Example theme={null}
use orbitflare_evm_sdk::{primitives::{address, b256}, BlockNumberOrTag, Filter, PolygonRpcClient, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = PolygonRpcClient::builder()
.url("https://ams.poly.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()?;
let transfer_topic =
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
let filter = Filter::new()
.from_block(60_000_000u64)
.to_block(BlockNumberOrTag::Latest)
.address(address!("0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"))
.event_signature(transfer_topic);
let logs = client.get_logs(&filter).await?;
println!("{} logs", logs.len());
Ok(())
}
```
### Contract calls and transactions
`call` and `estimate_gas` take alloy's `TransactionRequest`. To send a transaction, build and sign it with alloy (`alloy-signer`, `alloy-network`), then broadcast through the SDK:
```rust theme={null}
let hash = client.send_raw_transaction(&signed_tx_rlp).await?;
let receipt = client.get_transaction_receipt(hash).await?;
```
### Arbitrary methods
`request` calls any RPC method by name - the SDK builds the JSON-RPC envelope, handles retry and failover, and returns the `result` field. `request_raw` sends a raw JSON-RPC body string.
```rust theme={null}
use serde_json::json;
let syncing = client.request("eth_syncing", json!([])).await?;
```
## WebSocket
Enable the `ws` feature. `.build()` is async - it establishes the connection before returning.
```rust Example theme={null}
let client = PolygonWsClient::builder()
.url("wss://ams.poly.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()
.await?;
let mut heads = client.new_heads_subscribe().await?;
```
```rust Full Example theme={null}
use orbitflare_evm_sdk::{PolygonWsClient, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = PolygonWsClient::builder()
.url("wss://ams.poly.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()
.await?;
let mut heads = client.new_heads_subscribe().await?;
while let Some(head) = heads.next().await {
println!("block {} (gas used {})", head.number, head.gas_used);
}
Ok(())
}
```
The builder shares `.urls()`, `.fallback_url(s)`, `.api_key()`, and `.retry()` with the RPC builder; the WebSocket-specific options are `.ping_interval_secs(n)` (default 10) and `.max_missed_pongs(n)` (default 3).
### Subscriptions
Subscriptions are typed - each yields the corresponding alloy type instead of raw JSON:
| Method | Yields |
| - | - |
| `new_heads_subscribe()` | `Header` per new block |
| `logs_subscribe(&Filter)` | `Log` matching the filter |
| `new_pending_transactions_subscribe()` | `B256` transaction hash |
All subscriptions return a `WsSubscription`. Call `.next()` for the next typed event (`None` means the subscription was closed), or `.next_raw()` for the untyped `serde_json::Value` payload. `sub.unsubscribe().await` removes one explicitly; dropping a subscription also works.
### Reconnection
If the connection drops, the background task reconnects with exponential backoff and re-subscribes every active subscription automatically. Your `.next()` calls just keep working. Dead connections are detected via active ping/pong.
## Polygon gRPC
Polygon exposes a Bor gRPC interface for low-overhead block, header, and receipt access. Enable the `grpc` feature. gRPC runs over plaintext HTTP/2; authenticate with a token via `.api_key()` (sent as `x-token`) or use IP whitelisting.
```rust Example theme={null}
let client = PolygonGrpcClient::builder()
.url("http://your-bor-grpc-endpoint:3131")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()?;
let header = client.header_by_number(BlockNumber::Latest).await?;
```
```rust Full Example theme={null}
use orbitflare_evm_sdk::grpc::{BlockNumber, PolygonGrpcClient};
use orbitflare_evm_sdk::Result;
#[tokio::main]
async fn main() -> Result<()> {
let client = PolygonGrpcClient::builder()
.url("http://your-bor-grpc-endpoint:3131")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()?;
let header = client.header_by_number(BlockNumber::Latest).await?;
let author = client.author(header.number).await?;
println!("block {} authored by {author}", header.number);
Ok(())
}
```
| Method | Returns |
| - | - |
| `header_by_number(impl Into)` | `Header` |
| `block_by_number(impl Into)` | `Block` |
| `transaction_receipt(B256)` | `Receipt` |
| `bor_block_receipt(B256)` | `Receipt` |
| `author(impl Into)` | `Address` |
| `td_by_hash(B256)` / `td_by_number(...)` | `u64` total difficulty |
| `root_hash(start, end)` | `String` |
| `block_info_in_batch(start, end)` | `Vec` |
H160/H256 values convert to alloy `Address`/`B256` with the `ToAlloy` trait.
## Custom chains
Any EVM chain works: implement `Chain` for a marker type and point a generic `RpcClient` at its URL. `CHAIN_ID` is metadata (available as `RpcClient::::chain_id_const()`); it does not gate requests.
```rust Example theme={null}
struct Base;
impl Chain for Base {
const CHAIN_ID: u64 = 8453;
}
let client = RpcClient::::builder()
.url("https://mainnet.base.org")
.build()?;
```
```rust Full Example theme={null}
use orbitflare_evm_sdk::{Chain, RpcClient, Result};
struct Base;
impl Chain for Base {
const CHAIN_ID: u64 = 8453;
}
#[tokio::main]
async fn main() -> Result<()> {
let client = RpcClient::::builder()
.url("https://mainnet.base.org")
.build()?;
println!("chain id: {}", client.get_chain_id().await?);
println!("block: {}", client.get_block_number().await?);
Ok(())
}
```
## Arbitrum and Nitro block fields
Robinhood Chain is Arbitrum Nitro, so its blocks carry extra fields (`l1BlockNumber`, `sendRoot`, `sendCount`) that standard EVM block types drop. The SDK preserves them and exposes typed accessors via `NitroBlockExt`. Any other non-standard field is still available on `block.other`.
```rust Example theme={null}
let block = client
.get_block_by_number(client.block_tag(), false)
.await?
.expect("block");
let l1 = block.l1_block_number();
let send_root = block.send_root();
```
```rust Full Example theme={null}
use orbitflare_evm_sdk::{NitroBlockExt, RobinhoodRpcClient, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = RobinhoodRpcClient::builder()
.url("https://robinhood.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.build()?;
let block = client
.get_block_by_number(client.block_tag(), false)
.await?
.expect("block");
println!("l1 block: {:?}", block.l1_block_number());
println!("send root: {:?}", block.send_root());
println!("send count: {:?}", block.send_count());
Ok(())
}
```
Arbitrum precompiles (ArbSys, ArbGasInfo, ...) are reachable through `call()` like any contract.
## Endpoints
There are no default endpoints. Set the URL per client with `.url()`, or via an environment variable. Resolution order: `.url()` on the builder, then the environment variable.
| Chain | RPC | WebSocket |
| - | - | - |
| Polygon | `https://ams.poly.rpc.orbitflare.com` | `wss://ams.poly.rpc.orbitflare.com` |
| BNB Smart Chain | `https://bsc.rpc.orbitflare.com` | `wss://bsc.rpc.orbitflare.com` |
| Robinhood Chain | `https://robinhood.rpc.orbitflare.com` | `wss://robinhood.rpc.orbitflare.com` |
Use the exact endpoints from your OrbitFlare dashboard.
## Environment variables
| Variable | Used by | Purpose |
| - | - | - |
| `ORBITFLARE_LICENSE_KEY` | RPC, WebSocket, gRPC | API key appended to endpoint URLs (`x-token` for gRPC) |
| `ORBITFLARE_RPC_URL` | RPC | Endpoint used if `.url()` is not called |
| `ORBITFLARE_WS_URL` | WebSocket | Endpoint used if `.url()` is not called |
| `ORBITFLARE_GRPC_URL` | gRPC | Endpoint used if `.url()` is not called |
## Source
The SDK is open source: [github.com/orbitflare/orbitflare-evm-sdk-rs](https://github.com/orbitflare/orbitflare-evm-sdk-rs)
# Go gRPC Client (Yellowstone)
Source: https://docs.orbitflare.com/sdk/go-grpc
Yellowstone Geyser streaming in Go, with automatic reconnection, ping/pong liveness detection, and filters built programmatically via SubscribeRequestBuilder.
## Install
```bash theme={null}
go get github.com/orbitflare/orbitflare-sdk-go
```
```go theme={null}
import "github.com/orbitflare/orbitflare-sdk-go/geyser"
```
## Building the client
Here's a client with every option set:
```go theme={null}
import (
"time"
"github.com/orbitflare/orbitflare-sdk-go/geyser"
"github.com/orbitflare/orbitflare-sdk-go/orbitflare"
)
client, err := geyser.NewBuilder().
URL("http://ny.rpc.orbitflare.com:10000").
FallbackURL("http://fra.rpc.orbitflare.com:10000").
Retry(orbitflare.RetryPolicy{
InitialDelay: 100 * time.Millisecond,
MaxDelay: 30 * time.Second,
Multiplier: 2.0,
MaxAttempts: 0,
}).
Timeout(30 * time.Second).
Keepalive(60 * time.Second).
PingInterval(10 * time.Second).
MaxMissedPongs(3).
ChannelCapacity(4096).
Build()
```
Minimal setup:
```go theme={null}
client, err := geyser.NewBuilder().
URL("http://ny.rpc.orbitflare.com:10000").
Build()
```
gRPC needs no API key. The URL falls back to the `ORBITFLARE_GRPC_URL` environment variable.
### Builder methods
**`.URL(url)`** - The primary gRPC endpoint. Falls back to `ORBITFLARE_GRPC_URL`. Use `http://` for plaintext HTTP/2 and `https://` for TLS.
```go theme={null}
.URL("http://ny.rpc.orbitflare.com:10000")
```
**`.URLs(urls...)`** - Primary + fallbacks in one call. First element is primary.
```go theme={null}
.URLs("http://ny.rpc.orbitflare.com:10000", "http://fra.rpc.orbitflare.com:10000")
```
**`.FallbackURL(url)` / `.FallbackURLs(urls...)`** - Add fallback endpoints. On connection failure, the SDK rotates through them.
```go theme={null}
.FallbackURL("http://fra.rpc.orbitflare.com:10000")
```
**`.Retry(policy)`** - Controls reconnection backoff. When a connection drops, the SDK waits `InitialDelay`, then multiplies it each attempt (capped at `MaxDelay`). Set `MaxAttempts` to 0 for infinite retries. Default: 100ms initial, 30s max, 2x multiplier, infinite.
```go theme={null}
.Retry(orbitflare.RetryPolicy{
InitialDelay: 200 * time.Millisecond,
MaxDelay: 15 * time.Second,
Multiplier: 2.0,
MaxAttempts: 0,
})
```
**`.Timeout(duration)`** - Connection/dial timeout. Default: 30s.
```go theme={null}
.Timeout(15 * time.Second)
```
**`.Keepalive(duration)`** - TCP/HTTP2 keepalive interval, used to detect dead connections at the transport level. Default: 60s.
```go theme={null}
.Keepalive(30 * time.Second)
```
**`.PingInterval(duration)`** - How often the SDK sends proto-level `Ping` messages to the server. The server should respond with a `Pong`. Default: 10s.
```go theme={null}
.PingInterval(15 * time.Second)
```
**`.MaxMissedPongs(n)`** - How many consecutive pings can go unanswered before the SDK considers the connection dead and reconnects. Default: 3. With the defaults, a dead connection is detected within 30 seconds.
```go theme={null}
.MaxMissedPongs(5)
```
**`.ChannelCapacity(n)`** - The bounded channel between the background goroutine and your code. If your code is slow to consume events, the background goroutine blocks when this fills up instead of eating unlimited memory. Default: 4096.
```go theme={null}
.ChannelCapacity(8192)
```
**`.Logger(logger)`** - A `*slog.Logger` for connection lifecycle events. When unset, logging is controlled by `ORBITFLARE_LOG`.
## Building a subscription
Filters are built programmatically with `SubscribeRequestBuilder` and the typed filter types. A transaction matches if it involves any `AccountInclude` address; `AccountExclude` removes matches; `AccountRequired` means every listed address must be present.
```go theme={null}
request := geyser.NewSubscribeRequestBuilder().
Transactions("pumpfun", geyser.NewTransactionFilter().
Vote(false).
Failed(false).
AccountInclude("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")).
Accounts("usdc-holders", geyser.NewAccountFilter().
Owner("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").
Datasize(165).
Memcmp(geyser.MemcmpBase58(0, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")).
Lamports(geyser.LamportsGt(0))).
Slots("slots", geyser.NewSlotFilter().FilterByCommitment(true)).
Commitment(geyser.Confirmed).
Build()
```
### Filter types
* **`NewTransactionFilter()`** - `Vote(bool)`, `Failed(bool)`, `Signature(string)`, `AccountInclude(...)`, `AccountExclude(...)`, `AccountRequired(...)`.
* **`NewAccountFilter()`** - `Account(...)`, `Owner(...)`, `Datasize(uint64)`, `TokenAccountState(bool)`, `Memcmp(...)`, `Lamports(...)`, `NonemptyTxnSignature(bool)`.
* **`NewSlotFilter()`** - `FilterByCommitment(bool)`, `InterslotUpdates(bool)`.
* **`NewBlockFilter()`** - `AccountInclude(...)`, `IncludeTransactions(bool)`, `IncludeAccounts(bool)`, `IncludeEntries(bool)`.
* **`NewDeshredTransactionFilter()`** - `Vote(bool)`, `AccountInclude(...)`, `AccountExclude(...)`, `AccountRequired(...)`.
`Memcmp` is built with `geyser.MemcmpBytes(offset, []byte)`, `geyser.MemcmpBase58(offset, string)`, or `geyser.MemcmpBase64(offset, string)`. `Lamports` with `geyser.LamportsEq/Ne/Lt/Gt(uint64)`. `Commitment` is one of `geyser.Processed`, `geyser.Confirmed`, `geyser.Finalized`.
`Subscribe` also accepts a raw `*geyser.SubscribeRequest` (a re-export of the proto type) if you'd rather build it by hand.
## Reading the stream
`Subscribe` returns a `*geyser.Stream`. Call `Next(ctx)` to get the next update:
```go theme={null}
stream := client.Subscribe(request)
defer stream.Close()
for {
update, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
break // stream closed
}
if err != nil {
break // fatal: all retries exhausted
}
switch {
case update.GetTransaction() != nil:
tx := update.GetTransaction()
// tx.GetSlot(), tx.GetTransaction() (signature, accounts, instructions, meta)
case update.GetAccount() != nil:
acct := update.GetAccount()
// acct.GetSlot(), acct.GetAccount() (pubkey, lamports, owner, data), acct.GetIsStartup()
case update.GetSlot() != nil:
slot := update.GetSlot()
// slot.GetSlot(), slot.GetStatus()
case update.GetBlockMeta() != nil:
meta := update.GetBlockMeta()
// meta.GetSlot(), meta.GetBlockhash(), meta.GetParentSlot()
}
}
```
`Next(ctx)` blocks until an event arrives. It returns `io.EOF` once the stream has ended (including after `Close`); any other non-nil error is fatal for that stream. It also returns `ctx.Err()` if the context is cancelled. Pong messages are consumed internally and never appear in your stream.
### Closing
```go theme={null}
stream.Close()
```
Stops the background goroutine immediately. `client.Close()` stops every stream the client started.
## Multiple streams
One client can run many streams at once. Each gets its own background connection:
```go theme={null}
pumpfun := client.Subscribe(pumpfunReq)
raydium := client.Subscribe(raydiumReq)
slots := client.Subscribe(slotsReq)
```
They share endpoint health state - if one stream quarantines a failing endpoint, the others skip it on their next reconnect. But their connections and lifecycles are fully independent. You can spin up new streams at any time.
## Full example
A stream that watches pump.fun transactions and prints a summary for each one.
```go theme={null}
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"github.com/orbitflare/orbitflare-sdk-go/geyser"
)
func main() {
client, err := geyser.NewBuilder().
URL("http://ny.rpc.orbitflare.com:10000").
FallbackURL("http://fra.rpc.orbitflare.com:10000").
Build()
if err != nil {
log.Fatal(err)
}
defer client.Close()
request := geyser.NewSubscribeRequestBuilder().
Transactions("pumpfun", geyser.NewTransactionFilter().
Vote(false).
Failed(false).
AccountInclude("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")).
Slots("slots", geyser.NewSlotFilter().FilterByCommitment(true)).
Commitment(geyser.Confirmed).
Build()
stream := client.Subscribe(request)
defer stream.Close()
ctx := context.Background()
var count int
fmt.Println("streaming...")
for {
update, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
return
}
if err != nil {
log.Fatal(err)
}
switch {
case update.GetTransaction() != nil:
count++
tx := update.GetTransaction()
fmt.Printf("#%d slot=%d\n", count, tx.GetSlot())
case update.GetSlot() != nil:
s := update.GetSlot()
fmt.Printf("slot %d (%s)\n", s.GetSlot(), s.GetStatus())
}
}
}
```
# Go JetStream Client
Source: https://docs.orbitflare.com/sdk/go-jetstream
OrbitFlare's decoded shreds as gRPC streams in Go, with typed transaction filters that can be added and removed on a live stream.
## Install
```bash theme={null}
go get github.com/orbitflare/orbitflare-sdk-go
```
```go theme={null}
import "github.com/orbitflare/orbitflare-sdk-go/jetstream"
```
## Building the client
```go theme={null}
client, err := jetstream.NewBuilder().
URL("http://ny.jetstream.orbitflare.com").
FallbackURL("http://fra.jetstream.orbitflare.com").
Build()
```
The URL falls back to the `ORBITFLARE_JETSTREAM_URL` environment variable. All builder methods are identical to the [gRPC client](/sdk/go-grpc) - same defaults, same behavior (`Retry`, `Timeout`, `Keepalive`, `PingInterval`, `MaxMissedPongs`, `ChannelCapacity`, `Logger`).
## Building a subscription
JetStream supports transaction filters (no slots, blocks, or commitment - those are Yellowstone-specific). Each `TransactionFilter` exposes `AccountInclude`, `AccountExclude`, and `AccountRequired`.
```go theme={null}
request := jetstream.NewSubscribeRequestBuilder().
Transactions("raydium", jetstream.NewTransactionFilter().
AccountInclude("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")).
Transactions("pumpfun", jetstream.NewTransactionFilter().
AccountInclude("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")).
Build()
```
## Reading the stream
`Subscribe` returns a `*jetstream.Stream`. Call `Next(ctx)`:
```go theme={null}
stream := client.Subscribe(request)
defer stream.Close()
for {
update, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
break
}
if tx := update.GetTransaction(); tx != nil {
// tx.GetSlot() - the slot number
// tx.GetTransaction() - transaction info with signature, account_keys, instructions
}
}
```
Closing, multiple streams, reconnection, and ping/pong all work identically to the [gRPC client](/sdk/go-grpc).
## Full example
A stream that watches Raydium AMM swaps and prints each transaction's slot.
```go theme={null}
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"github.com/orbitflare/orbitflare-sdk-go/jetstream"
)
func main() {
client, err := jetstream.NewBuilder().
URL("http://ny.jetstream.orbitflare.com").
Build()
if err != nil {
log.Fatal(err)
}
defer client.Close()
request := jetstream.NewSubscribeRequestBuilder().
Transactions("raydium", jetstream.NewTransactionFilter().
AccountInclude("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")).
Build()
stream := client.Subscribe(request)
defer stream.Close()
ctx := context.Background()
var count int
fmt.Println("streaming raydium txs...")
for {
update, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
return
}
if err != nil {
log.Fatal(err)
}
if tx := update.GetTransaction(); tx != nil {
count++
fmt.Printf("#%d slot=%d\n", count, tx.GetSlot())
}
}
}
```
## JetStream v2
JetStream v2 (`github.com/orbitflare/orbitflare-sdk-go/jetstream/v2`) runs on the **same endpoints and authentication** as v1 and is fully additive: v1 keeps working unchanged. What v2 adds:
* **Runtime-managed filters** - add and remove filters on a live stream without reconnecting.
* **Per-message sequence numbers** - every response carries a monotonic `Sequence`, so you can detect dropped messages.
* **Opt-in enrichment** - request fee payer, program ids, compute-unit price, compute limit, resolved address-table addresses, and more, per transaction.
* **Slot lifecycle events** - a separate server stream of slot alive/complete/dead events.
The v2 client lives under `jetstream/v2`. The builder is identical to v1 (same methods, defaults, `ORBITFLARE_JETSTREAM_URL` env var, and failover):
```go theme={null}
import jetstreamv2 "github.com/orbitflare/orbitflare-sdk-go/jetstream/v2"
client, err := jetstreamv2.NewBuilder().
URL("http://ny.jetstream.orbitflare.com").
FallbackURL("http://fra.jetstream.orbitflare.com").
Build()
```
### Building typed filters
Each filter is a `TransactionFilter` with a client-chosen id (via `.WithID()`). The id is echoed back on every matching transaction and in the filter-validation acknowledgement, so you can correlate matches and remove the filter later.
```go theme={null}
filter := jetstreamv2.NewTransactionFilter().
AccountInclude("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P").
IncludeEnrichment(true).
WithID("pumpfun")
```
A filter must set at least one of `AccountInclude`, `AccountExclude`, or `AccountRequired`; empty (match-everything) filters are rejected. `IncludeEnrichment` is optional (default off) and applies to the whole subscription: if any active filter enables it, every transaction you receive is enriched.
### Subscribing to transactions
```go theme={null}
stream := client.SubscribeTransactions(filter)
defer stream.Close()
for {
resp, err := stream.Next(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
break
}
switch {
case resp.GetTransaction() != nil:
ft := resp.GetTransaction()
// resp.GetSequence() - monotonic per-stream sequence number
// ft.GetFilterIds() - which of your filters this tx matched
if tx := ft.GetTransaction(); tx != nil {
// tx.GetSlot(), tx.GetSignature(), tx.GetAccountKeys(), tx.GetInstructions()
// enrichment (only when IncludeEnrichment is set): tx.GetFeePayer(),
// tx.GetProgramIds(), tx.GetComputeUnitPrice(), tx.GetComputeLimit(),
// tx.GetLoadedWritableAddresses(), tx.GetLoadedReadonlyAddresses()
fmt.Printf("seq=%d slot=%d\n", resp.GetSequence(), tx.GetSlot())
}
case resp.GetFilterValidation() != nil:
r := resp.GetFilterValidation() // one per filter after each add/remove
fmt.Printf("filter %s accepted=%v %s\n", r.GetFilterId(), r.GetAccepted(), r.GetRejectionReason())
case resp.GetHeartbeat() != nil:
// resp.GetHeartbeat().GetServerTsMs() - server wall-clock, ms since epoch
case resp.GetPong() != nil:
// resp.GetPong().GetPingId()
}
}
```
### Managing filters on a live stream
Take a handle from the stream and add or remove filters without reconnecting. Removals reference the filter id you assigned with `.WithID()`.
```go theme={null}
handle := stream.Handle()
handle.AddFilters([]*jetstreamv2.TxFilter{
jetstreamv2.NewTransactionFilter().
AccountInclude("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8").
WithID("raydium"),
})
handle.RemoveFilters([]string{"pumpfun"})
```
`stream.AddFilters(...)` and `stream.RemoveFilters(...)` are shortcuts for the same thing. Each returns an error if the stream has been closed.
### Slot lifecycle events
`SubscribeSlots()` is a separate server stream and takes no filters.
```go theme={null}
slots := client.SubscribeSlots()
defer slots.Close()
for {
event, err := slots.Next(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
break
}
// event.GetSlot(), event.GetStatus() (a SlotStatus enum: alive / complete / dead),
// event.GetParentSlot(), event.GetCurrentLeader(), event.GetSequence()
fmt.Printf("slot=%d status=%s\n", event.GetSlot(), event.GetStatus())
}
```
### Health probes
`GetVersion(ctx)` and `Ping(ctx, count)` are unary calls with the same failover as the streams.
```go theme={null}
version, err := client.GetVersion(ctx)
count, err := client.Ping(ctx, 7)
```
For the full protobuf specification, message shapes, enrichment fields, and slot statuses, see the [JetStream Protocol Reference](/data-streaming/jetstream-reference).
# Go RPC Client
Source: https://docs.orbitflare.com/sdk/go-rpc
Solana JSON-RPC client for Go from the orbitflare-sdk-go module, with retry, failover, and typed helpers for common methods.
## Install
```bash theme={null}
go get github.com/orbitflare/orbitflare-sdk-go
```
```go theme={null}
import "github.com/orbitflare/orbitflare-sdk-go/rpc"
```
## Building the client
Here's a client with every option set:
```go theme={null}
import (
"time"
"github.com/orbitflare/orbitflare-sdk-go/orbitflare"
"github.com/orbitflare/orbitflare-sdk-go/rpc"
)
client, err := rpc.NewBuilder().
URL("http://ny.rpc.orbitflare.com").
FallbackURL("http://fra.rpc.orbitflare.com").
FallbackURL("http://ams.rpc.orbitflare.com").
APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN").
Commitment("confirmed").
Retry(orbitflare.RetryPolicy{
InitialDelay: 100 * time.Millisecond,
MaxDelay: 30 * time.Second,
Multiplier: 2.0,
MaxAttempts: 5,
}).
Timeout(30 * time.Second).
Build()
```
Most of these have sensible defaults. A minimal setup:
```go theme={null}
client, err := rpc.NewBuilder().
URL("http://ny.rpc.orbitflare.com").
Build()
```
Every method takes a `context.Context`, so pass a context with a deadline to bound a call:
```go theme={null}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
```
### Builder methods
**`.URL(url)`** - The primary endpoint to send requests to. If not set, the SDK checks the `ORBITFLARE_RPC_URL` environment variable.
```go theme={null}
.URL("http://ny.rpc.orbitflare.com")
```
**`.URLs(urls...)`** - Set the primary and all fallbacks in one call. The first element is the primary, the rest are fallbacks.
```go theme={null}
.URLs("http://ny.rpc.orbitflare.com", "http://fra.rpc.orbitflare.com", "http://ams.rpc.orbitflare.com")
```
**`.FallbackURL(url)`** - Add a single fallback endpoint. Call multiple times to add several. When the primary fails, the SDK tries fallbacks in order.
```go theme={null}
.FallbackURL("http://fra.rpc.orbitflare.com").
FallbackURL("http://ams.rpc.orbitflare.com")
```
**`.FallbackURLs(urls...)`** - Same as `FallbackURL` but takes several at once.
```go theme={null}
.FallbackURLs("http://fra.rpc.orbitflare.com", "http://ams.rpc.orbitflare.com")
```
**`.APIKey(key)`** - Your OrbitFlare license key. If not set, the SDK checks `ORBITFLARE_LICENSE_KEY` from the environment. The key is injected into the URL at request time, never stored in the endpoint, and never printed in logs or errors.
```go theme={null}
.APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN")
```
**`.Commitment(level)`** - Default commitment level used for all typed helpers. Options: `"processed"`, `"confirmed"`, `"finalized"`. Defaults to `"confirmed"`.
```go theme={null}
.Commitment("finalized")
```
**`.Retry(policy)`** - Controls how the SDK retries failed requests. `InitialDelay` is the wait before the first retry. `Multiplier` scales the delay on each attempt. `MaxDelay` caps the backoff. `MaxAttempts` limits total retries (0 means infinite). Defaults: 100ms initial, 30s max, 2x multiplier, infinite attempts.
```go theme={null}
.Retry(orbitflare.RetryPolicy{
InitialDelay: 200 * time.Millisecond,
MaxDelay: 15 * time.Second,
Multiplier: 2.0,
MaxAttempts: 5,
})
```
**`.Timeout(duration)`** - HTTP timeout for each individual request. Defaults to 30 seconds.
```go theme={null}
.Timeout(10 * time.Second)
```
**`.Logger(logger)`** - A `*slog.Logger` for request retries and failovers. When unset, logging is controlled by the `ORBITFLARE_LOG` environment variable (`trace|debug|info|warn|error|silent`, default `silent`).
```go theme={null}
.Logger(slog.New(slog.NewTextHandler(os.Stderr, nil)))
```
## Available RPC methods
Typed helpers return Go values; everything else returns `json.RawMessage` you can unmarshal into your own types.
### `GetSlot(ctx)`
Returns the current slot number.
```go theme={null}
slot, err := client.GetSlot(ctx) // uint64
```
### `GetBalance(ctx, address)`
Returns the balance in lamports for an account.
```go theme={null}
lamports, err := client.GetBalance(ctx, "So11111111111111111111111111111111111111112") // uint64
sol := float64(lamports) / 1_000_000_000
```
### `GetAccountInfo(ctx, address)`
Returns the full account data as `json.RawMessage`, or nil if the account doesn't exist. Data is base64-encoded.
```go theme={null}
raw, err := client.GetAccountInfo(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
if raw != nil {
var info struct {
Owner string `json:"owner"`
Lamports uint64 `json:"lamports"`
}
json.Unmarshal(raw, &info)
}
```
### `GetMultipleAccounts(ctx, addresses)`
Fetches multiple accounts in one call. Automatically chunks into batches of 100 (Solana's per-request limit), so you can pass any number of addresses.
```go theme={null}
accounts, err := client.GetMultipleAccounts(ctx, []string{"addr1", "addr2", "addr3"}) // []json.RawMessage
```
### `GetLatestBlockhash(ctx)`
Returns the most recent blockhash and the last block height it's valid for.
```go theme={null}
blockhash, lastValid, err := client.GetLatestBlockhash(ctx) // (string, uint64)
```
### `GetTransaction(ctx, signature)`
Fetches a confirmed transaction by its signature. Returns the full transaction with metadata as `json.RawMessage`.
```go theme={null}
raw, err := client.GetTransaction(ctx, "5K8F2j...")
```
### `GetSignaturesForAddress(ctx, address, limit)`
Returns recent transaction signatures for an address, newest first.
```go theme={null}
sigs, err := client.GetSignaturesForAddress(ctx, "So111...", 10) // []json.RawMessage
```
### `GetProgramAccounts(ctx, programID)`
Returns all accounts owned by a program. Can return a lot of data.
```go theme={null}
accounts, err := client.GetProgramAccounts(ctx, "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
```
### `GetRecentPrioritizationFees(ctx, addresses)`
Returns recent priority fees for a set of accounts. Useful for estimating compute unit pricing.
```go theme={null}
fees, err := client.GetRecentPrioritizationFees(ctx, []string{"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"})
```
### `SendTransaction(ctx, txBase64)`
Sends a signed transaction to the network. Takes a base64-encoded serialized transaction. Returns the transaction signature.
```go theme={null}
signature, err := client.SendTransaction(ctx, txBase64) // string
```
### `SimulateTransaction(ctx, txBase64)`
Simulates a transaction without submitting it. Returns logs, compute units consumed, and any errors as `json.RawMessage`.
```go theme={null}
raw, err := client.SimulateTransaction(ctx, txBase64)
```
### `GetTokenAccountsByOwner(ctx, owner, mint, programID)`
Returns token accounts for a wallet. Pass either a specific mint or a token program ID. If both are empty strings, defaults to the SPL Token program.
```go theme={null}
tokens, err := client.GetTokenAccountsByOwner(ctx, "wallet_address", "", "")
```
### `GetTransactionsForAddress(ctx, address, options)`
OrbitFlare-specific method that combines `getSignaturesForAddress` and `getTransaction` into one call. Returns a `GetTransactionsResult` with the `Data` slice and an optional `PaginationToken` for fetching the next page.
Supports four detail levels (`signatures`, `none`, `accounts`, `full`), bidirectional sorting, time/slot filtering, status filtering, and token account inclusion. `options` may be nil for defaults.
```go theme={null}
details, order, status := "full", "asc", "succeeded"
var limit uint32 = 100
gte, lte := int64(1704067200), int64(1706745600)
result, err := client.GetTransactionsForAddress(ctx, "address", &rpc.GetTransactionsOptions{
TransactionDetails: &details,
Limit: &limit,
SortOrder: &order,
Filters: &rpc.GetTransactionsFilters{
Status: &status,
BlockTime: &rpc.RangeFilter[int64]{Gte: >e, Lte: <e},
},
})
for _, tx := range result.Data {
_ = tx // json.RawMessage
}
if result.PaginationToken != nil {
// fetch next page by setting PaginationToken on the next call
}
```
### `Request(ctx, method, params)`
Call any RPC method by name. The SDK builds the JSON-RPC envelope, handles retry and failover, and returns the `result` field as `json.RawMessage`.
```go theme={null}
epoch, err := client.Request(ctx, "getEpochInfo", []any{})
inflation, err := client.Request(ctx, "getInflationRate", []any{})
```
### `RequestRaw(ctx, body)`
Send a raw JSON-RPC body string. Same retry and failover as everything else.
```go theme={null}
result, err := client.RequestRaw(ctx, `{"jsonrpc":"2.0","id":1,"method":"getHealth","params":[]}`)
```
## Full example
A program that checks the state of the network, fetches a wallet's SOL balance, and looks up recent transactions.
```go theme={null}
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/orbitflare/orbitflare-sdk-go/rpc"
)
func main() {
client, err := rpc.NewBuilder().
URL("http://ny.rpc.orbitflare.com").
FallbackURL("http://fra.rpc.orbitflare.com").
Commitment("confirmed").
Build()
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
wallet := "CKs1E69a2e9TmH4mKKLrXFF8kD3ZnwKjoEuXa6sz9WqX"
slot, _ := client.GetSlot(ctx)
blockhash, _, _ := client.GetLatestBlockhash(ctx)
fmt.Printf("network slot: %d\nblockhash: %s\n", slot, blockhash)
balance, _ := client.GetBalance(ctx, wallet)
fmt.Printf("\n%s\n SOL: %.4f\n", wallet, float64(balance)/1_000_000_000)
sigs, _ := client.GetSignaturesForAddress(ctx, wallet, 5)
fmt.Println("\nrecent transactions:")
for _, raw := range sigs {
var sig struct {
Signature string `json:"signature"`
Slot uint64 `json:"slot"`
Err any `json:"err"`
}
json.Unmarshal(raw, &sig)
status := "ok"
if sig.Err != nil {
status = "failed"
}
fmt.Printf(" %d %s %s\n", sig.Slot, status, sig.Signature[:20])
}
}
```
# Go WebSocket Client
Source: https://docs.orbitflare.com/sdk/go-websocket
Real-time Solana subscriptions in Go from the orbitflare-sdk-go module: account changes, logs, slots, and signatures with auto-reconnect.
## Install
```bash theme={null}
go get github.com/orbitflare/orbitflare-sdk-go
```
```go theme={null}
import "github.com/orbitflare/orbitflare-sdk-go/ws"
```
## Building the client
```go theme={null}
import (
"time"
"github.com/orbitflare/orbitflare-sdk-go/orbitflare"
"github.com/orbitflare/orbitflare-sdk-go/ws"
)
client, err := ws.NewBuilder().
URL("ws://ny.rpc.orbitflare.com").
FallbackURL("ws://fra.rpc.orbitflare.com").
APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN").
Retry(orbitflare.RetryPolicy{
InitialDelay: 100 * time.Millisecond,
MaxDelay: 30 * time.Second,
Multiplier: 2.0,
MaxAttempts: 0,
}).
PingInterval(10 * time.Second).
MaxMissedPongs(3).
Build(ctx)
```
Minimal:
```go theme={null}
client, err := ws.NewBuilder().
URL("ws://ny.rpc.orbitflare.com").
Build(ctx)
```
Note that `.Build(ctx)` establishes the WebSocket connection before returning - the passed context governs the initial dial. If the connection fails, you get the error immediately. The client then runs until `Close()`.
### Builder methods
**`.URL(url)`** - Primary WebSocket endpoint. Falls back to `ORBITFLARE_WS_URL` env var.
```go theme={null}
.URL("ws://ny.rpc.orbitflare.com")
```
**`.URLs(urls...)`** - Primary + fallbacks in one call.
```go theme={null}
.URLs("ws://ny.rpc.orbitflare.com", "ws://fra.rpc.orbitflare.com")
```
**`.FallbackURL(url)` / `.FallbackURLs(urls...)`** - Add failover endpoints. On reconnect, the SDK rotates through them.
```go theme={null}
.FallbackURL("ws://fra.rpc.orbitflare.com")
```
**`.APIKey(key)`** - License key. Falls back to `ORBITFLARE_LICENSE_KEY` env var. Injected into the URL at connect time, never printed in logs or errors.
```go theme={null}
.APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN")
```
**`.Retry(policy)`** - Reconnection backoff. Default: 100ms initial, 30s max, 2x multiplier, infinite attempts.
```go theme={null}
.Retry(orbitflare.RetryPolicy{
InitialDelay: 200 * time.Millisecond,
MaxDelay: 15 * time.Second,
Multiplier: 2.0,
MaxAttempts: 0,
})
```
**`.PingInterval(duration)`** - How often the SDK sends WebSocket `Ping` frames. Default: 10s.
```go theme={null}
.PingInterval(15 * time.Second)
```
**`.MaxMissedPongs(n)`** - Pings without a response before killing the connection. Default: 3.
```go theme={null}
.MaxMissedPongs(5)
```
**`.Logger(logger)`** - A `*slog.Logger` for connection lifecycle events. When unset, controlled by `ORBITFLARE_LOG`.
## Available subscriptions
### `SlotSubscribe(ctx)`
Fires every time a slot is processed, confirmed, or finalized.
```go theme={null}
sub, err := client.SlotSubscribe(ctx)
```
Each event is JSON with `slot`, `parent`, and `root` fields:
```json theme={null}
{"slot": 413014740, "parent": 413014739, "root": 413014708}
```
### `AccountSubscribe(ctx, address, commitment)`
Fires when the specified account's data changes.
```go theme={null}
sub, err := client.AccountSubscribe(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "confirmed")
```
Returns base64-encoded account data with lamports, owner, and executable flag.
### `LogsSubscribe(ctx, mentions, commitment)`
Fires for transactions that mention the given addresses. Pass an empty slice for all transactions.
```go theme={null}
sub, err := client.LogsSubscribe(ctx, []string{"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}, "confirmed")
```
All logs:
```go theme={null}
sub, err := client.LogsSubscribe(ctx, nil, "confirmed")
```
### `SignatureSubscribe(ctx, signature, commitment)`
Fires once when a transaction reaches the given commitment level. Useful for confirming a transaction you just sent.
```go theme={null}
sub, err := client.SignatureSubscribe(ctx, "5K8F2j...", "confirmed")
```
## Reading events
All subscriptions return a `*ws.Subscription`. Call `Next(ctx)` to get the next event:
```go theme={null}
for {
event, ok := sub.Next(ctx)
if !ok {
break // context cancelled or client shut down
}
fmt.Println(string(event))
}
```
`Next(ctx)` returns `(json.RawMessage, bool)`. When `ok` is false the subscription is finished (the context was cancelled or the client closed).
## Unsubscribing
```go theme={null}
sub.Unsubscribe()
```
This sends an unsubscribe message to the server. If you stop reading a subscription without calling this, the SDK detects the orphan and sends the unsubscribe automatically.
## Multiple subscriptions
All subscriptions run on a single WebSocket connection. The SDK routes notifications to the right subscription internally.
```go theme={null}
slots, _ := client.SlotSubscribe(ctx)
usdc, _ := client.AccountSubscribe(ctx, "EPjFWdd5...", "confirmed")
logs, _ := client.LogsSubscribe(ctx, []string{"6EF8r..."}, "confirmed")
```
You can add new subscriptions at any time while existing ones are running.
## Reconnection
If the connection drops, the background goroutine reconnects with exponential backoff and re-subscribes everything automatically. Your `Next(ctx)` calls just keep working - events resume once the connection is back.
## Full example
A monitoring program that subscribes to slot updates and USDC account changes, printing a live feed.
```go theme={null}
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/orbitflare/orbitflare-sdk-go/ws"
)
func main() {
ctx := context.Background()
client, err := ws.NewBuilder().
URL("ws://ny.rpc.orbitflare.com").
Build(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Close()
slots, err := client.SlotSubscribe(ctx)
if err != nil {
log.Fatal(err)
}
defer slots.Unsubscribe()
fmt.Println("watching slots...")
for {
event, ok := slots.Next(ctx)
if !ok {
return
}
var slot struct {
Slot uint64 `json:"slot"`
Parent uint64 `json:"parent"`
}
json.Unmarshal(event, &slot)
fmt.Printf("slot %d (parent %d)\n", slot.Slot, slot.Parent)
}
}
```
# OrbitFlare SDKs
Source: https://docs.orbitflare.com/sdk/overview
Official client libraries for building on OrbitFlare. RPC, gRPC (Yellowstone), JetStream, and WebSocket - with built-in retry, failover, and reconnection.
The OrbitFlare SDK gives you typed clients for every OrbitFlare service. Instead of managing raw HTTP requests, gRPC channels, and WebSocket connections yourself, the SDK handles connection lifecycle, authentication, retries, failover, and reconnection behind a clean API.
## What's included
| Service | What it does |
| - | - |
| **RPC** | JSON-RPC client with typed helpers for common Solana methods, plus raw escape hatches for anything custom |
| **WebSocket** | Subscriptions for account changes, logs, slots, and signatures with auto-resubscribe on reconnect |
| **gRPC (Yellowstone)** | Yellowstone Geyser streaming - transactions, accounts, slots, blocks. YAML config or programmatic filters |
| **JetStream** | OrbitFlare's decoded shreds delivered as gRPC streams. Same client pattern as Yellowstone, different proto. v1 (fixed filters) and v2 (runtime-managed filters, sequence numbers, opt-in enrichment, slot events) |
## Environment variables
| Variable | Used by | Purpose |
| - | - | - |
| `ORBITFLARE_LICENSE_KEY` | RPC, WebSocket | API key appended to endpoint URLs |
| `ORBITFLARE_RPC_URL` | RPC | Default endpoint if `.url()` is not called |
| `ORBITFLARE_WS_URL` | WebSocket | Default endpoint if `.url()` is not called |
| `ORBITFLARE_GRPC_URL` | gRPC | Default endpoint if `.url()` is not called |
| `ORBITFLARE_JETSTREAM_URL` | JetStream | Default endpoint if `.url()` is not called |
## Robinhood Chain SDK
For [Robinhood Chain](/robinhood-chain), OrbitFlare ships a separate Rust crate, `orbitflare-robinhood-sdk`, built on alloy types with the same transport features - retry, endpoint failover, and self-healing WebSocket subscriptions. See the [Robinhood Chain SDK](/sdk/robinhood) page.
## Source
The SDKs are open source:
* Rust: [github.com/orbitflare/orbitflare-sdk-rs](https://github.com/orbitflare/orbitflare-sdk-rs)
* TypeScript: [github.com/orbitflare/orbitflare-sdk-ts](https://github.com/orbitflare/orbitflare-sdk-ts)
* Go: [github.com/orbitflare/orbitflare-sdk-go](https://github.com/orbitflare/orbitflare-sdk-go)
* Robinhood Chain (Rust): [github.com/orbitflare/orbitflare-robinhood-sdk-rs](https://github.com/orbitflare/orbitflare-robinhood-sdk-rs)
# Rust SDK
Source: https://docs.orbitflare.com/sdk/robinhood
Rust SDK for Robinhood Chain, the orbitflare-robinhood-sdk crate.
`orbitflare-robinhood-sdk` is deprecated. Robinhood Chain is now supported by the [EVM SDK](/sdk/evm) (`orbitflare-evm-sdk`) via `RobinhoodRpcClient` / `RobinhoodWsClient`, which also covers Polygon, BNB, and custom chains. The RPC and WebSocket surface is the same. This crate will receive no further updates.
Built on [alloy](https://github.com/alloy-rs/alloy) types throughout - `Address`, `U256`, `B256`, `Filter`, `TransactionRequest`, typed blocks, transactions, receipts, and logs - with OrbitFlare's own transport: endpoint failover, retry with backoff, and self-healing WebSocket subscriptions. It targets [Robinhood Chain](/robinhood-chain), the EVM-compatible L2, and defaults to OrbitFlare's production endpoints.
## Install
```bash theme={null}
cargo add orbitflare-robinhood-sdk
```
Only the RPC client is enabled by default. Enable what you need:
```bash theme={null}
cargo add orbitflare-robinhood-sdk --features ws
cargo add orbitflare-robinhood-sdk --features all
```
## Alloy types
All addresses, hashes, and quantities are alloy types. The common ones are re-exported at the crate root (`Address`, `U256`, `B256`, `Bytes`, `Filter`, `TransactionRequest`, `BlockNumberOrTag`, ...), and the full crates are available as `orbitflare_robinhood_sdk::primitives` (alloy-primitives) and `orbitflare_robinhood_sdk::rpc_types` (alloy-rpc-types-eth).
```rust theme={null}
use orbitflare_robinhood_sdk::primitives::{address, b256, utils::format_ether};
```
## RPC client
Here's a client with every option set:
```rust theme={null}
use orbitflare_robinhood_sdk::{BlockNumberOrTag, Result, RetryPolicy, RpcClientBuilder};
use std::time::Duration;
let client = RpcClientBuilder::new()
.url("https://robinhood.rpc.orbitflare.com")
.fallback_urls(&["https://robinhood-backup.rpc.orbitflare.com"])
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.block_tag(BlockNumberOrTag::Finalized)
.retry(RetryPolicy {
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
multiplier: 2.0,
max_attempts: 5,
})
.timeout(Duration::from_secs(30))
.build()?;
```
Everything has a sensible default - the builder defaults to the production OrbitFlare endpoint, so the minimal setup is:
```rust theme={null}
let client = RpcClientBuilder::new().build()?;
```
### Builder methods
**`.url(url)`** - The primary endpoint. Resolution order: `.url()` on the builder, then the `ORBITFLARE_ROBINHOOD_RPC_URL` environment variable, then the default `https://robinhood.rpc.orbitflare.com` (`rpc::DEFAULT_RPC_URL`).
**`.urls(&[...])`** - Set the primary and all fallbacks in one call. The first element is the primary, the rest are fallbacks.
**`.fallback_url(url)` / `.fallback_urls(&[...])`** - Add failover endpoints. When the primary fails, the SDK tries fallbacks in order. Failing endpoints are quarantined with exponential cooldown (10s, 20s, 40s, max 60s) and automatically retried once the cooldown expires; healthy endpoints are always preferred.
**`.api_key(key)`** - Your OrbitFlare license key. If not set, the SDK checks `ORBITFLARE_LICENSE_KEY` from the environment. The key is appended to the endpoint URL at request time.
**`.block_tag(tag)`** - Default block tag used by state queries (`get_balance`, `call`, `get_code`, ...). Takes anything that converts into `BlockNumberOrTag`. Defaults to `Latest`.
**`.retry(policy)`** - Controls retry on transient errors (5xx, 429, connection resets, JSON-RPC error code -32005) with exponential backoff before failing over to the next endpoint. 429 responses with a `Retry-After` header are respected.
**`.timeout(duration)`** - HTTP timeout for each individual request.
### Available RPC methods
| Method | Returns |
| - | - |
| `get_block_number()` | `u64` |
| `get_chain_id()` | `u64` |
| `get_balance(Address)` | `U256` wei |
| `get_transaction_count(Address)` | `u64` nonce |
| `get_gas_price()` | `u128` wei |
| `max_priority_fee_per_gas()` | `u128` wei |
| `get_block_by_number(impl Into, full_txs)` | `Option` |
| `get_transaction_by_hash(B256)` | `Option` |
| `get_transaction_receipt(B256)` | `Option` |
| `get_logs(&Filter)` | `Vec` |
| `get_code(Address)` | `Bytes` |
| `call(&TransactionRequest)` | `Bytes` |
| `estimate_gas(&TransactionRequest)` | `u64` |
| `send_raw_transaction(&[u8])` | `B256` tx hash |
| `fee_history(blocks, newest, percentiles)` | `FeeHistory` |
| `request(method, params)` | Any RPC method by name (`serde_json::Value`) |
| `request_raw(body)` | Raw JSON-RPC body string |
### Reading chain state
```rust theme={null}
use orbitflare_robinhood_sdk::primitives::{address, utils::format_ether};
let block = client.get_block_number().await?;
let gas_price = client.get_gas_price().await?;
let wallet = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
let balance = client.get_balance(wallet).await?;
let nonce = client.get_transaction_count(wallet).await?;
println!("ETH: {}", format_ether(balance));
```
### Log filters
`get_logs` takes alloy's `Filter` directly:
```rust theme={null}
use orbitflare_robinhood_sdk::primitives::{address, b256};
use orbitflare_robinhood_sdk::{BlockNumberOrTag, Filter};
let transfer_topic =
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
let filter = Filter::new()
.from_block(1_000_000u64)
.to_block(BlockNumberOrTag::Latest)
.address(address!("d0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC"))
.event_signature(transfer_topic);
let logs = client.get_logs(&filter).await?;
```
### Contract calls and transactions
`call` and `estimate_gas` take alloy's `TransactionRequest`. To send a transaction, build and sign it with alloy (`alloy-signer`, `alloy-network`), then broadcast through the SDK:
```rust theme={null}
let hash = client.send_raw_transaction(&signed_tx_rlp).await?;
let receipt = client.get_transaction_receipt(hash).await?;
```
### Arbitrary methods
`request` calls any RPC method by name - the SDK builds the JSON-RPC envelope, handles retry and failover, and returns the `result` field. This also covers the `arb_*` extension methods Robinhood Chain serves as an Arbitrum Nitro chain. `request_raw` sends a raw JSON-RPC body string.
```rust theme={null}
use serde_json::json;
let syncing = client.request("eth_syncing", json!([])).await?;
let version = client
.request_raw(r#"{"jsonrpc":"2.0","id":1,"method":"web3_clientVersion","params":[]}"#)
.await?;
```
## WebSocket client
Enable the `ws` feature. Robinhood Chain produces blocks roughly every 100 milliseconds, so `newHeads` fires far faster than on Ethereum mainnet or most L2s.
```rust theme={null}
use orbitflare_robinhood_sdk::{Result, RetryPolicy, WsClientBuilder};
use std::time::Duration;
let client = WsClientBuilder::new()
.url("wss://robinhood.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.retry(RetryPolicy {
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
multiplier: 2.0,
max_attempts: 0,
})
.ping_interval_secs(10)
.max_missed_pongs(3)
.build()
.await?;
```
Minimal:
```rust theme={null}
let client = WsClientBuilder::new().build().await?;
```
Note that `.build()` is async - it establishes the WebSocket connection before returning. The builder shares `.urls()`, `.fallback_url(s)()`, `.api_key()`, and `.retry()` with the RPC builder; the WebSocket-specific options are:
**`.url(url)`** - Primary WebSocket endpoint. Resolution order: `.url()`, then `ORBITFLARE_ROBINHOOD_WS_URL`, then the default `wss://robinhood.rpc.orbitflare.com` (`ws::DEFAULT_WS_URL`).
**`.ping_interval_secs(n)`** - How often the SDK sends WebSocket `Ping` frames to detect dead connections. Default: 10.
**`.max_missed_pongs(n)`** - Pings without a response before the connection is considered dead and reconnected. Default: 3.
### Subscriptions
Subscriptions are typed - each yields the corresponding alloy type instead of raw JSON:
| Method | Yields |
| - | - |
| `new_heads_subscribe()` | `Header` per new block |
| `logs_subscribe(&Filter)` | `Log` matching the filter |
| `new_pending_transactions_subscribe()` | `B256` transaction hash |
```rust theme={null}
use orbitflare_robinhood_sdk::primitives::b256;
use orbitflare_robinhood_sdk::Filter;
let mut heads = client.new_heads_subscribe().await?;
let transfer_topic =
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
let mut transfers = client
.logs_subscribe(&Filter::new().event_signature(transfer_topic))
.await?;
```
All subscriptions return a `WsSubscription`. Call `.next()` for the next typed event (`None` means the subscription was closed), or `.next_raw()` for the untyped `serde_json::Value` payload.
```rust theme={null}
while let Some(head) = heads.next().await {
println!("block {} (gas used {})", head.number, head.gas_used);
}
```
All subscriptions run on a single WebSocket connection, and you can add new ones at any time. `sub.unsubscribe().await` removes one explicitly; dropping a subscription without calling it also works - the SDK detects the orphan and sends the unsubscribe automatically.
### Reconnection
If the connection drops, the background task reconnects with exponential backoff and re-subscribes every active subscription automatically. Your `.next()` calls just keep working - events resume once the connection is back. Dead connections are detected via active ping/pong, configurable with `.ping_interval_secs()` and `.max_missed_pongs()`.
## Environment variables
| Variable | Used by | Purpose |
| - | - | - |
| `ORBITFLARE_LICENSE_KEY` | RPC, WebSocket | API key appended to endpoint URLs |
| `ORBITFLARE_ROBINHOOD_RPC_URL` | RPC | Overrides the default endpoint if `.url()` is not called |
| `ORBITFLARE_ROBINHOOD_WS_URL` | WebSocket | Overrides the default endpoint if `.url()` is not called |
## Full example
A monitoring script that reads a wallet, then follows new blocks and ERC-20 transfers in real time:
```rust theme={null}
use orbitflare_robinhood_sdk::primitives::{address, b256, utils::format_ether};
use orbitflare_robinhood_sdk::{Filter, Result, RpcClientBuilder, WsClientBuilder};
#[tokio::main]
async fn main() -> Result<()> {
let rpc = RpcClientBuilder::new().build()?;
let wallet = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
let balance = rpc.get_balance(wallet).await?;
let block = rpc.get_block_number().await?;
println!("block {block}, wallet holds {} ETH", format_ether(balance));
let ws = WsClientBuilder::new().build().await?;
let mut heads = ws.new_heads_subscribe().await?;
let transfer_topic = b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
let mut transfers = ws
.logs_subscribe(&Filter::new().event_signature(transfer_topic))
.await?;
println!("watching new heads and ERC-20 transfers...");
loop {
tokio::select! {
Some(head) = heads.next() => {
println!("block {} (gas used {})", head.number, head.gas_used);
}
Some(log) = transfers.next() => {
let tx = log.transaction_hash.unwrap_or_default();
println!("transfer on {} in {tx}", log.address());
}
}
}
}
```
## Source
The SDK is open source: [github.com/orbitflare/orbitflare-robinhood-sdk-rs](https://github.com/orbitflare/orbitflare-robinhood-sdk-rs)
# Rust gRPC Client (Yellowstone)
Source: https://docs.orbitflare.com/sdk/rust-grpc
Yellowstone Geyser streaming in Rust via the orbitflare-sdk grpc feature, with automatic reconnection, ping/pong liveness detection, and YAML-configured filters.
## Install
```bash theme={null}
cargo add orbitflare-sdk --features grpc
```
## Building the client
Here's a client with every option set:
```rust theme={null}
use orbitflare_sdk::{GeyserClientBuilder, RetryPolicy, Result};
use std::time::Duration;
let client = GeyserClientBuilder::new()
.url("http://ny.rpc.orbitflare.com:10000")
.fallback_url("http://fra.rpc.orbitflare.com:10000")
.retry(RetryPolicy {
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
multiplier: 2.0,
max_attempts: 0,
})
.timeout_secs(30)
.keepalive_secs(60)
.ping_interval_secs(10)
.max_missed_pongs(3)
.channel_capacity(4096)
.build()?;
```
Minimal setup:
```rust theme={null}
let client = GeyserClientBuilder::new()
.url("http://ny.rpc.orbitflare.com:10000")
.build()?;
```
### Builder methods
**`.url(url)`** - The primary gRPC endpoint. Falls back to `ORBITFLARE_GRPC_URL` env var.
```rust theme={null}
.url("http://ny.rpc.orbitflare.com:10000")
```
**`.urls(&[...])`** - Primary + fallbacks in one call. First element is primary.
```rust theme={null}
.urls(&["http://ny.rpc.orbitflare.com:10000", "http://fra.rpc.orbitflare.com:10000"])
```
**`.fallback_url(url)` / `.fallback_urls(&[...])`** - Add fallback endpoints. On connection failure, the SDK rotates through them.
```rust theme={null}
.fallback_url("http://fra.rpc.orbitflare.com:10000")
```
**`.retry(policy)`** - Controls reconnection backoff. When a connection drops, the SDK waits `initial_delay`, then doubles it each attempt (capped at `max_delay`). Set `max_attempts` to 0 for infinite retries. Default: 100ms initial, 30s max, 2x multiplier, infinite.
```rust theme={null}
.retry(RetryPolicy {
initial_delay: Duration::from_millis(200),
max_delay: Duration::from_secs(15),
multiplier: 2.0,
max_attempts: 0,
})
```
**`.timeout_secs(n)`** - Per-request gRPC timeout. Default: 30.
```rust theme={null}
.timeout_secs(15)
```
**`.keepalive_secs(n)`** - TCP keepalive interval. The OS sends probes at this interval to detect dead connections at the TCP level. Default: 60.
```rust theme={null}
.keepalive_secs(30)
```
**`.ping_interval_secs(n)`** - How often the SDK sends proto-level `Ping` messages to the server. The server should respond with a `Pong`. Default: 10.
```rust theme={null}
.ping_interval_secs(15)
```
**`.max_missed_pongs(n)`** - How many consecutive pings can go unanswered before the SDK considers the connection dead and reconnects. Default: 3. With the defaults, a dead connection is detected within 30 seconds.
```rust theme={null}
.max_missed_pongs(5)
```
**`.channel_capacity(n)`** - The bounded channel between the background task and your code. If your code is slow to consume events, the background task blocks when this fills up instead of eating unlimited memory. Default: 4096.
```rust theme={null}
.channel_capacity(8192)
```
## Writing a YAML config
Create a YAML file with the filters you want:
```yaml theme={null}
# grpc.yml
transactions:
pumpfun:
vote: false
failed: false
account_include:
- "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
jupiter:
vote: false
failed: false
account_include:
- "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"
accounts:
usdc_mint:
account:
- "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
token_program:
owner:
- "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
slots:
all:
filter_by_commitment: true
commitment: confirmed
```
### YAML filter reference
**`transactions`** - each entry is a named filter. A transaction matches if it involves any of the `account_include` addresses. `account_exclude` removes matches. `account_required` means all listed addresses must be present. `vote` and `failed` filter by transaction type.
**`accounts`** - watch specific accounts by address, or watch all accounts owned by a program via `owner`.
**`slots`** - subscribe to slot updates. `filter_by_commitment` only sends updates at your commitment level.
**`commitment`** - `"processed"`, `"confirmed"`, or `"finalized"`. Defaults to `"confirmed"`.
The YAML supports `${ENV_VAR}` expansion:
```yaml theme={null}
transactions:
target:
account_include:
- "${TARGET_PROGRAM}"
```
## Subscribing and reading events
### From YAML
```rust theme={null}
let mut stream = client.subscribe_yaml("grpc.yml")?;
```
### Programmatically
For dynamic filters built at runtime:
```rust theme={null}
use std::collections::HashMap;
use orbitflare_sdk::proto::geyser::*;
let mut filters = HashMap::new();
filters.insert("target".into(), SubscribeRequestFilterTransactions {
vote: Some(false),
failed: Some(false),
signature: None,
account_include: vec![some_address.to_string()],
account_exclude: vec![],
account_required: vec![],
});
let request = SubscribeRequest {
transactions: filters,
commitment: Some(1),
ping: Some(SubscribeRequestPing { id: 1 }),
..Default::default()
};
let mut stream = client.subscribe(request);
```
### Reading the stream
Both `subscribe_yaml` and `subscribe` return a `GeyserStream`. Call `.next()` to get the next event:
```rust theme={null}
use orbitflare_sdk::proto::geyser::subscribe_update::UpdateOneof;
while let Some(update) = stream.next().await {
let update = update?;
match update.update_oneof {
Some(UpdateOneof::Transaction(tx)) => {
// tx.slot - the slot this transaction was in
// tx.transaction - the transaction info (signature, accounts, instructions, meta)
}
Some(UpdateOneof::Account(acct)) => {
// acct.slot - the slot
// acct.account - account info (pubkey, lamports, owner, data)
// acct.is_startup - true during initial account snapshot
}
Some(UpdateOneof::Slot(slot)) => {
// slot.slot - the slot number
// slot.status - processed, confirmed, finalized, etc.
}
Some(UpdateOneof::BlockMeta(meta)) => {
// meta.slot, meta.blockhash, meta.parent_slot
// meta.executed_transaction_count
}
_ => {}
}
}
```
`stream.next()` blocks until an event arrives. Returns `None` when the stream is closed. Each event is a `Result` - an `Err` means all retries were exhausted and the connection is permanently gone.
Pong messages are consumed internally and never appear in your stream.
### Closing
```rust theme={null}
stream.close();
```
Stops the background task immediately. Or just drop the stream.
## Multiple streams
One client can run many streams at once. Each gets its own background connection:
```rust theme={null}
let mut pumpfun = client.subscribe_yaml("config/pumpfun.yml")?;
let mut raydium = client.subscribe_yaml("config/raydium.yml")?;
let mut slots = client.subscribe_yaml("config/slots.yml")?;
```
They share endpoint health state - if one stream quarantines a failing endpoint, the others skip it on their next reconnect. But their connections and lifecycles are fully independent.
You can spin up new streams at any time, including dynamically based on data from an existing stream.
## Full example
A stream that watches pump.fun transactions, decodes the signature, and prints a summary for each one.
```rust theme={null}
use orbitflare_sdk::{GeyserClientBuilder, Result};
use orbitflare_sdk::proto::geyser::subscribe_update::UpdateOneof;
#[tokio::main]
async fn main() -> Result<()> {
let client = GeyserClientBuilder::new()
.url("http://ny.rpc.orbitflare.com:10000")
.fallback_url("http://fra.rpc.orbitflare.com:10000")
.build()?;
let mut stream = client.subscribe_yaml("grpc.yml")?;
let mut tx_count: u64 = 0;
println!("streaming...");
while let Some(update) = stream.next().await {
let update = update?;
match update.update_oneof {
Some(UpdateOneof::Transaction(tx)) => {
tx_count += 1;
if let Some(info) = &tx.transaction {
let sig = bs58::encode(&info.signature).into_string();
let fee = info.meta.as_ref().map(|m| m.fee).unwrap_or(0);
let ix_count = info.transaction
.as_ref()
.and_then(|t| t.message.as_ref())
.map(|m| m.instructions.len())
.unwrap_or(0);
println!(
"#{tx_count} slot={} sig={}... fee={fee} instructions={ix_count}",
tx.slot,
&sig[..16],
);
}
}
Some(UpdateOneof::Slot(slot)) => {
println!("slot {} ({:?})", slot.slot, slot.status);
}
_ => {}
}
}
Ok(())
}
```
With this `grpc.yml`:
```yaml theme={null}
transactions:
pumpfun:
vote: false
failed: false
account_include:
- "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
slots:
all:
filter_by_commitment: true
commitment: confirmed
```
# Rust JetStream Client
Source: https://docs.orbitflare.com/sdk/rust-jetstream
OrbitFlare's decoded shreds as gRPC streams in Rust, with transaction and account filters configured in YAML.
## Install
```bash theme={null}
cargo add orbitflare-sdk --features jetstream
```
## Building the client
```rust theme={null}
use orbitflare_sdk::{JetstreamClientBuilder, RetryPolicy, Result};
use std::time::Duration;
let client = JetstreamClientBuilder::new()
.url("http://ny.jetstream.orbitflare.com")
.fallback_url("http://fra.jetstream.orbitflare.com")
.retry(RetryPolicy {
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
multiplier: 2.0,
max_attempts: 0,
})
.timeout_secs(30)
.keepalive_secs(60)
.ping_interval_secs(10)
.max_missed_pongs(3)
.channel_capacity(4096)
.build()?;
```
Minimal:
```rust theme={null}
let client = JetstreamClientBuilder::new()
.url("http://ny.jetstream.orbitflare.com")
.build()?;
```
URL falls back to `ORBITFLARE_JETSTREAM_URL` env var. All builder methods are identical to the [gRPC client](/sdk/rust-grpc) - same defaults, same behavior.
## Writing a YAML config
JetStream supports transaction and account filters. No slots, blocks, or commitment - those are Yellowstone-specific.
```yaml theme={null}
# jetstream.yml
transactions:
raydium:
account_include:
- "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
pumpfun:
account_include:
- "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
accounts:
my_wallet:
account:
- "YOUR_WALLET_ADDRESS"
owner:
- "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
```
### YAML filter reference
**`transactions`** - named filters. `account_include` matches transactions involving those addresses. `account_exclude` removes matches. `account_required` means all listed addresses must appear.
**`accounts`** - watch specific addresses with `account`, or all accounts owned by a program with `owner`.
Supports `${ENV_VAR}` expansion.
## Subscribing and reading events
### From YAML
```rust theme={null}
let mut stream = client.subscribe_yaml("jetstream.yml")?;
```
### Programmatically
```rust theme={null}
use std::collections::HashMap;
use orbitflare_sdk::proto::jetstream::*;
let mut filters = HashMap::new();
filters.insert("target".into(), SubscribeRequestFilterTransactions {
account_include: vec![some_address.to_string()],
account_exclude: vec![],
account_required: vec![],
});
let request = SubscribeRequest {
transactions: filters,
accounts: HashMap::new(),
ping: Some(SubscribeRequestPing { id: 1 }),
};
let mut stream = client.subscribe(request);
```
### With the typed builder
`SubscribeRequestBuilder` and `TransactionFilter` build the same request without hand-writing proto. Each `TransactionFilter` exposes `account_include`, `account_exclude`, and `account_required`, and accepts any iterator of string-like values.
```rust theme={null}
use orbitflare_sdk::jetstream::{SubscribeRequestBuilder, TransactionFilter};
let request = SubscribeRequestBuilder::new()
.transactions(
"pumpfun",
TransactionFilter::new()
.account_include(["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"])
.account_required(["So11111111111111111111111111111111111111112"]),
)
.build();
let mut stream = client.subscribe(request);
```
### Reading the stream
```rust theme={null}
use orbitflare_sdk::proto::jetstream::subscribe_update::UpdateOneof;
while let Some(update) = stream.next().await {
let update = update?;
match update.update_oneof {
Some(UpdateOneof::Transaction(tx)) => {
// tx.slot - the slot number
// tx.transaction - transaction info with signature, account_keys,
// instructions, address_table_lookups
}
Some(UpdateOneof::Account(acct)) => {
// acct.slot - the slot
// acct.account - account info (pubkey, lamports, owner, data)
// acct.is_startup - true during initial snapshot
}
_ => {}
}
}
```
Closing, multiple streams, reconnection, and ping/pong all work identically to the [gRPC client](/sdk/rust-grpc).
## Full example
A stream that watches Raydium AMM swaps and prints each transaction's signature and instruction count.
```rust theme={null}
use orbitflare_sdk::{JetstreamClientBuilder, Result};
use orbitflare_sdk::proto::jetstream::subscribe_update::UpdateOneof;
#[tokio::main]
async fn main() -> Result<()> {
let client = JetstreamClientBuilder::new()
.url("http://ny.jetstream.orbitflare.com")
.build()?;
let mut stream = client.subscribe_yaml("jetstream.yml")?;
let mut count: u64 = 0;
println!("streaming raydium txs...");
while let Some(update) = stream.next().await {
let update = update?;
if let Some(UpdateOneof::Transaction(tx)) = update.update_oneof {
count += 1;
if let Some(info) = &tx.transaction {
let sig = bs58::encode(&info.signature).into_string();
let num_ix = info.instructions.len();
let num_accounts = info.account_keys.len();
println!(
"#{count} slot={} sig={}... ix={num_ix} accounts={num_accounts}",
tx.slot,
&sig[..16],
);
}
}
}
Ok(())
}
```
With this `jetstream.yml`:
```yaml theme={null}
transactions:
raydium:
account_include:
- "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
```
## JetStream v2
JetStream v2 (`orbitflare_sdk::jetstream::v2`) runs on the **same endpoints and authentication** as v1 and is fully additive: v1 keeps working unchanged. What v2 adds:
* **Runtime-managed filters** - add and remove filters on a live stream without reconnecting.
* **Per-message sequence numbers** - every response carries a monotonic `sequence`, so you can detect dropped messages.
* **Opt-in enrichment** - request fee payer, program ids, compute-unit price, compute limit, resolved address-table addresses, and more, per transaction.
* **Slot lifecycle events** - a separate server stream of slot alive/complete/dead events.
The v2 client lives under `jetstream::v2`. The builder is identical to v1 (same methods, defaults, `ORBITFLARE_JETSTREAM_URL` env var, and failover):
```rust theme={null}
use orbitflare_sdk::jetstream::v2::{JetstreamClientBuilder, TransactionFilter};
let client = JetstreamClientBuilder::new()
.url("http://ny.jetstream.orbitflare.com")
.fallback_url("http://fra.jetstream.orbitflare.com")
.build()?;
```
### Building typed filters
Each filter is a `TransactionFilter` with a client-chosen id (via `.with_id()`). The id is echoed back on every matching transaction and in the filter-validation acknowledgement, so you can correlate matches and remove the filter later.
```rust theme={null}
let filter = TransactionFilter::new()
.account_include(["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"])
.include_enrichment(true)
.with_id("pumpfun");
```
A filter must set at least one of `account_include`, `account_exclude`, or `account_required`; empty (match-everything) filters are rejected. `include_enrichment` is optional (default off) and applies to the whole subscription: if any active filter enables it, every transaction you receive is enriched.
### Subscribing to transactions
```rust theme={null}
use orbitflare_sdk::proto::jetstream::v2::subscribe_transactions_response::Payload;
let mut stream = client.subscribe_transactions(vec![filter]);
while let Some(resp) = stream.next().await {
let resp = resp?;
match resp.payload {
Some(Payload::Transaction(ft)) => {
// resp.sequence - monotonic per-stream sequence number
// ft.filter_ids - which of your filters this tx matched
if let Some(tx) = &ft.transaction {
// tx.slot, tx.signature, tx.account_keys, tx.instructions
// enrichment (only when include_enrichment is set): tx.fee_payer,
// tx.program_ids, tx.compute_unit_price, tx.compute_limit,
// tx.loaded_writable_addresses, tx.loaded_readonly_addresses
println!("seq={} slot={} cu_price={}", resp.sequence, tx.slot, tx.compute_unit_price);
}
}
Some(Payload::FilterValidation(r)) => {
// one per filter after each add/remove
println!("filter {} accepted={} {}", r.filter_id, r.accepted, r.rejection_reason);
}
Some(Payload::Heartbeat(hb)) => {
// hb.server_ts_ms - server wall-clock, ms since epoch
let _ = hb;
}
Some(Payload::Pong(p)) => {
// p.ping_id
let _ = p;
}
None => {}
}
}
```
### Managing filters on a live stream
Take a handle from the stream and add or remove filters without reconnecting. Removals reference the filter id you assigned with `.with_id()`.
```rust theme={null}
let handle = stream.handle();
handle.add_filters(vec![
TransactionFilter::new()
.account_include(["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"])
.with_id("raydium"),
])?;
handle.remove_filters(vec!["pumpfun".to_string()])?;
```
### Slot lifecycle events
`subscribe_slots()` is a separate server stream and takes no filters.
```rust theme={null}
let mut slots = client.subscribe_slots();
while let Some(event) = slots.next().await {
let event = event?;
// event.slot, event.status() (a SlotStatus enum: alive / complete / dead),
// event.parent_slot, event.current_leader, event.sequence
println!("slot={} status={:?}", event.slot, event.status());
}
```
### Health probes
`get_version()` and `ping()` are unary calls with the same failover as the streams.
```rust theme={null}
println!("version={}", client.get_version().await?);
println!("ping={}", client.ping(7).await?);
```
For the full protobuf specification, message shapes, enrichment fields, and slot statuses, see the [JetStream Protocol Reference](/data-streaming/jetstream-reference).
# Rust RPC Client
Source: https://docs.orbitflare.com/sdk/rust-rpc
Solana JSON-RPC client for Rust from the orbitflare-sdk crate, with retry, failover, and typed helpers for common methods.
## Install
```bash theme={null}
cargo add orbitflare-sdk
```
The `rpc` feature is enabled by default.
## Building the client
Here's a client with every option set:
```rust theme={null}
use orbitflare_sdk::{RpcClientBuilder, RetryPolicy, Result};
use std::time::Duration;
let client = RpcClientBuilder::new()
.url("http://ny.rpc.orbitflare.com")
.fallback_url("http://fra.rpc.orbitflare.com")
.fallback_url("http://ams.rpc.orbitflare.com")
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
.commitment("confirmed")
.retry(RetryPolicy {
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
multiplier: 2.0,
max_attempts: 5,
})
.timeout(Duration::from_secs(30))
.build()?;
```
Most of these have sensible defaults. A minimal setup:
```rust theme={null}
let client = RpcClientBuilder::new()
.url("http://ny.rpc.orbitflare.com")
.build()?;
```
### Builder methods
**`.url(url)`** - The primary endpoint to send requests to. If not set, the SDK checks the `ORBITFLARE_RPC_URL` environment variable.
```rust theme={null}
.url("http://ny.rpc.orbitflare.com")
```
**`.urls(&[...])`** - Set the primary and all fallbacks in one call. The first element is the primary, the rest are fallbacks.
```rust theme={null}
.urls(&["http://ny.rpc.orbitflare.com", "http://fra.rpc.orbitflare.com", "http://ams.rpc.orbitflare.com"])
```
**`.fallback_url(url)`** - Add a single fallback endpoint. Call multiple times to add several. When the primary fails, the SDK tries fallbacks in order.
```rust theme={null}
.fallback_url("http://fra.rpc.orbitflare.com")
.fallback_url("http://ams.rpc.orbitflare.com")
```
**`.fallback_urls(&[...])`** - Same as `fallback_url` but takes a slice.
```rust theme={null}
.fallback_urls(&["http://fra.rpc.orbitflare.com", "http://ams.rpc.orbitflare.com"])
```
**`.api_key(key)`** - Your OrbitFlare license key. If not set, the SDK checks `ORBITFLARE_LICENSE_KEY` from the environment. The key is injected into the URL at request time, never stored in the endpoint.
```rust theme={null}
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
```
**`.commitment(level)`** - Default commitment level used for all typed helpers. Options: `"processed"`, `"confirmed"`, `"finalized"`. Defaults to `"confirmed"`.
```rust theme={null}
.commitment("finalized")
```
**`.retry(policy)`** - Controls how the SDK retries failed requests. `initial_delay` is the wait before the first retry. `multiplier` scales the delay on each attempt. `max_delay` caps the backoff. `max_attempts` limits total retries per endpoint (0 means infinite). Defaults: 100ms initial, 30s max, 2x multiplier, infinite attempts.
```rust theme={null}
use orbitflare_sdk::RetryPolicy;
use std::time::Duration;
.retry(RetryPolicy {
initial_delay: Duration::from_millis(200),
max_delay: Duration::from_secs(15),
multiplier: 2.0,
max_attempts: 5,
})
```
**`.timeout(duration)`** - HTTP timeout for each individual request. Defaults to 30 seconds.
```rust theme={null}
.timeout(Duration::from_secs(10))
```
## Available RPC methods
### `get_slot()`
Returns the current slot number.
```rust theme={null}
let slot: u64 = client.get_slot().await?;
```
### `get_balance(address)`
Returns the balance in lamports for an account.
```rust theme={null}
let lamports: u64 = client.get_balance("So11111111111111111111111111111111111111112").await?;
let sol = lamports as f64 / 1_000_000_000.0;
```
### `get_account_info(address)`
Returns the full account data or `None` if the account doesn't exist. Data is base64-encoded.
```rust theme={null}
let account: Option = client.get_account_info("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").await?;
if let Some(info) = account {
println!("owner: {}", info["owner"]);
println!("lamports: {}", info["lamports"]);
println!("data length: {}", info["data"][0].as_str().unwrap_or("").len());
}
```
### `get_multiple_accounts(addresses)`
Fetches multiple accounts in one call. Automatically chunks into batches of 100 (Solana's per-request limit), so you can pass any number of addresses.
```rust theme={null}
let accounts: Vec