> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitflare.com/llms.txt
> Use this file to discover all available pages before exploring further.

# RPC-клиент

> JSON-RPC-клиент с повторными попытками, failover и типизированными хелперами для распространённых методов Solana.

## Установка

```bash theme={null}
cargo add orbitflare-sdk
```

Флаг `rpc` включён по умолчанию.

## Сборка клиента

Пример клиента со всеми опциями:

```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()?;
```

Большинство параметров имеют разумные значения по умолчанию. Минимальная конфигурация:

```rust theme={null}
let client = RpcClientBuilder::new()
    .url("http://ny.rpc.orbitflare.com")
    .build()?;
```

### Методы билдера

**`.url(url)`** — основной эндпоинт для отправки запросов. Если не задан, SDK читает переменную окружения `ORBITFLARE_RPC_URL`.

```rust theme={null}
.url("http://ny.rpc.orbitflare.com")
```

**`.urls(&[...])`** — задать основной эндпоинт и все резервные за один вызов. Первый элемент — основной, остальные — fallback.

```rust theme={null}
.urls(&["http://ny.rpc.orbitflare.com", "http://fra.rpc.orbitflare.com", "http://ams.rpc.orbitflare.com"])
```

**`.fallback_url(url)`** — добавить один резервный эндпоинт. Вызывайте несколько раз, чтобы добавить несколько штук. При сбое основного SDK пробует fallback по порядку.

```rust theme={null}
.fallback_url("http://fra.rpc.orbitflare.com")
.fallback_url("http://ams.rpc.orbitflare.com")
```

**`.fallback_urls(&[...])`** — то же, что `fallback_url`, но принимает срез.

```rust theme={null}
.fallback_urls(&["http://fra.rpc.orbitflare.com", "http://ams.rpc.orbitflare.com"])
```

**`.api_key(key)`** — лицензионный ключ OrbitFlare. Если не задан, SDK читает `ORBITFLARE_LICENSE_KEY`. Ключ подставляется в URL при запросе, в самом объекте эндпоинта не хранится.

```rust theme={null}
.api_key("ORBIT-XXXXXX-NNNNNN-NNNNNN")
```

**`.commitment(level)`** — уровень commitment по умолчанию для всех типизированных хелперов. Допустимые значения: `"processed"`, `"confirmed"`, `"finalized"`. По умолчанию `"confirmed"`.

```rust theme={null}
.commitment("finalized")
```

**`.retry(policy)`** — как SDK повторяет неудачные запросы. `initial_delay` — ожидание перед первым повтором. `multiplier` — множитель задержки на каждую попытку. `max_delay` — верхний предел backoff. `max_attempts` — лимит повторов на эндпоинт (`0` — бесконечно). По умолчанию: начальная задержка 100 мс, максимум 30 с, множитель 2×, бесконечные попытки.

```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-таймаут каждого отдельного запроса. По умолчанию 30 секунд.

```rust theme={null}
.timeout(Duration::from_secs(10))
```

## Доступные методы RPC

### `get_slot()`

Возвращает текущий номер слота.

```rust theme={null}
let slot: u64 = client.get_slot().await?;
```

### `get_balance(address)`

Возвращает баланс в лампортах для аккаунта.

```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)`

Возвращает полные данные аккаунта или `None`, если аккаунта нет. Данные закодированы в base64.

```rust theme={null}
let account: Option<serde_json::Value> = 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)`

Загружает несколько аккаунтов одним вызовом. Автоматически режет на батчи по 100 (лимит Solana на запрос), поэтому можно передать любое число адресов.

```rust theme={null}
let accounts: Vec<Option<serde_json::Value>> = client
    .get_multiple_accounts(&["addr1", "addr2", "addr3"])
    .await?;

for (i, acct) in accounts.iter().enumerate() {
    match acct {
        Some(info) => println!("account {i}: {} lamports", info["lamports"]),
        None => println!("account {i}: not found"),
    }
}
```

### `get_latest_blockhash()`

Возвращает последний blockhash и последнюю высоту блока, на которую он действителен.

```rust theme={null}
let (blockhash, last_valid_block_height): (String, u64) = client.get_latest_blockhash().await?;
```

### `get_transaction(signature)`

Получает подтверждённую транзакцию по подписи. Возвращает полную транзакцию с метаданными.

```rust theme={null}
let tx: serde_json::Value = client.get_transaction("5K8F2j...").await?;
println!("slot: {}", tx["slot"]);
println!("fee: {}", tx["meta"]["fee"]);
```

### `get_signatures_for_address(address, limit)`

Возвращает недавние подписи транзакций для адреса, сначала самые новые.

```rust theme={null}
let sigs: Vec<serde_json::Value> = client
    .get_signatures_for_address("So111...", 10)
    .await?;

for sig in &sigs {
    println!("{} at slot {}", sig["signature"], sig["slot"]);
}
```

### `get_program_accounts(program_id)`

Возвращает все аккаунты, принадлежащие программе. Может вернуть много данных.

```rust theme={null}
let accounts: Vec<serde_json::Value> = client
    .get_program_accounts("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
    .await?;
```

### `get_recent_prioritization_fees(addresses)`

Возвращает недавние приоритетные комиссии для набора аккаунтов. Полезно для оценки цены compute unit.

```rust theme={null}
let fees: Vec<serde_json::Value> = client
    .get_recent_prioritization_fees(&["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"])
    .await?;

for fee in &fees {
    println!("slot {}: {} micro-lamports", fee["slot"], fee["prioritizationFee"]);
}
```

### `send_transaction(tx_base64)`

Отправляет подписанную транзакцию в сеть. Принимает сериализованную транзакцию в base64. Возвращает подпись транзакции.

```rust theme={null}
let signature: String = client.send_transaction(tx_base64).await?;
println!("sent: {signature}");
```

### `simulate_transaction(tx_base64)`

Симулирует транзакцию без отправки. Возвращает логи, потреблённые compute units и ошибки, если есть.

```rust theme={null}
let result: serde_json::Value = client.simulate_transaction(tx_base64).await?;
if let Some(err) = result["err"].as_str() {
    println!("simulation failed: {err}");
} else {
    println!("compute units: {}", result["unitsConsumed"]);
}
```

### `get_token_accounts_by_owner(owner, mint, program_id)`

Возвращает token-аккаунты кошелька. Передайте конкретный mint или ID программы токенов. Если оба `None`, по умолчанию используется программа SPL Token.

```rust theme={null}
let tokens: Vec<serde_json::Value> = client
    .get_token_accounts_by_owner("wallet_address", None, None)
    .await?;

for token in &tokens {
    let info = &token["account"]["data"]["parsed"]["info"];
    println!("mint: {} balance: {}",
        info["mint"],
        info["tokenAmount"]["uiAmountString"],
    );
}
```

### `get_transactions_for_address(address, options)`

Специфичный для OrbitFlare метод, объединяющий `getSignaturesForAddress` и `getTransaction` в один вызов. Возвращает `GetTransactionsResult` с массивом `data` и опциональным `pagination_token` для следующей страницы.

Поддерживаются четыре уровня детализации (`signatures`, `none`, `accounts`, `full`), двунаправленная сортировка, фильтрация по времени/слоту, по статусу и включение token-аккаунтов.

```rust theme={null}
use orbitflare_sdk::{GetTransactionsOptions, GetTransactionsFilters, RangeFilter};

let result = client.get_transactions_for_address(
    "address",
    GetTransactionsOptions::new()
        .transaction_details("full")
        .limit(100)
        .sort_order("asc")
        .filters(
            GetTransactionsFilters::new()
                .token_accounts("all")
                .status("succeeded")
                .block_time(RangeFilter {
                    gte: Some(1704067200),
                    lte: Some(1706745600),
                    ..Default::default()
                })
        ),
).await?;

for tx in &result.data {
    println!("slot {}", tx["slot"]);
}

if let Some(token) = result.pagination_token {
    // fetch next page using .pagination_token(&token)
}
```

### `request(method, params)`

Вызов любого RPC-метода по имени. SDK собирает JSON-RPC-обёртку, применяет retry и failover, возвращает поле `result`.

```rust theme={null}
use serde_json::json;

let epoch = client.request("getEpochInfo", json!([])).await?;
let inflation = client.request("getInflationRate", json!([])).await?;
let supply = client.request("getSupply", json!([])).await?;
```

### `request_raw(body)`

Отправка сырого тела JSON-RPC. Те же retry и failover, что и везде.

```rust theme={null}
let result = client
    .request_raw(r#"{"jsonrpc":"2.0","id":1,"method":"getHealth","params":[]}"#)
    .await?;
```

## Полный пример

Скрипт проверяет состояние сети, баланс SOL и токены кошелька, смотрит недавние транзакции.

```rust theme={null}
use orbitflare_sdk::{RpcClientBuilder, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let client = RpcClientBuilder::new()
        .url("http://ny.rpc.orbitflare.com")
        .fallback_url("http://fra.rpc.orbitflare.com")
        .commitment("confirmed")
        .build()?;

    let wallet = "CKs1E69a2e9TmH4mKKLrXFF8kD3ZnwKjoEuXa6sz9WqX";

    let slot = client.get_slot().await?;
    let (blockhash, _) = client.get_latest_blockhash().await?;
    println!("network slot: {slot}");
    println!("blockhash: {blockhash}");

    let balance = client.get_balance(wallet).await?;
    println!("\n{wallet}");
    println!("  SOL: {:.4}", balance as f64 / 1_000_000_000.0);

    let tokens = client.get_token_accounts_by_owner(wallet, None, None).await?;
    for token in &tokens {
        let info = &token["account"]["data"]["parsed"]["info"];
        let mint = info["mint"].as_str().unwrap_or("unknown");
        let amount = info["tokenAmount"]["uiAmountString"].as_str().unwrap_or("0");
        if amount != "0" {
            println!("  {mint}: {amount}");
        }
    }

    let sigs = client.get_signatures_for_address(wallet, 5).await?;
    println!("\nrecent transactions:");
    for sig in &sigs {
        let signature = sig["signature"].as_str().unwrap_or("?");
        let slot = sig["slot"].as_u64().unwrap_or(0);
        let err = if sig["err"].is_null() { "ok" } else { "failed" };
        println!("  {slot} {err} {}", &signature[..20]);
    }

    let fees = client
        .get_recent_prioritization_fees(&[wallet])
        .await?;
    let avg: f64 = fees.iter()
        .filter_map(|f| f["prioritizationFee"].as_f64())
        .sum::<f64>() / fees.len().max(1) as f64;
    println!("\navg priority fee: {avg:.0} micro-lamports");

    Ok(())
}
```
