> ## 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.

# Rust SDK

> Rust SDK для Robinhood Chain: крейт orbitflare-robinhood-sdk.

Повсюду используются типы [alloy](https://github.com/alloy-rs/alloy) — `Address`, `U256`, `B256`, `Filter`, `TransactionRequest`, типизированные блоки, транзакции, квитанции и логи — с собственным транспортом OrbitFlare: failover эндпоинтов, повторные попытки с backoff и самовосстанавливающиеся WebSocket-подписки. SDK ориентирован на [Robinhood Chain](/ru/robinhood-chain), EVM-совместимый L2, и по умолчанию использует продакшен-эндпоинты OrbitFlare.

## Установка

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

По умолчанию включён только RPC-клиент. Включите то, что нужно:

```bash theme={null}
cargo add orbitflare-robinhood-sdk --features ws
cargo add orbitflare-robinhood-sdk --features all
```

## Типы alloy

Все адреса, хеши и числовые величины — типы alloy. Самые распространённые реэкспортированы в корне крейта (`Address`, `U256`, `B256`, `Bytes`, `Filter`, `TransactionRequest`, `BlockNumberOrTag`, ...), а полные крейты доступны как `orbitflare_robinhood_sdk::primitives` (alloy-primitives) и `orbitflare_robinhood_sdk::rpc_types` (alloy-rpc-types-eth).

```rust theme={null}
use orbitflare_robinhood_sdk::primitives::{address, b256, utils::format_ether};
```

## RPC-клиент

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

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

У всего есть разумные значения по умолчанию — билдер по умолчанию использует продакшен-эндпоинт OrbitFlare, поэтому минимальная конфигурация:

```rust theme={null}
let client = RpcClientBuilder::new().build()?;
```

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

**`.url(url)`** — основной эндпоинт. Порядок разрешения: `.url()` в билдере, затем переменная окружения `ORBITFLARE_ROBINHOOD_RPC_URL`, затем значение по умолчанию `https://robinhood.rpc.orbitflare.com` (`rpc::DEFAULT_RPC_URL`).

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

**`.fallback_url(url)` / `.fallback_urls(&[...])`** — добавить резервные эндпоинты для failover. При сбое основного SDK пробует fallback по порядку. Сбоящие эндпоинты помещаются в карантин с экспоненциальным периодом ожидания (10 с, 20 с, 40 с, максимум 60 с) и автоматически пробуются снова по его истечении; здоровые эндпоинты всегда в приоритете.

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

**`.block_tag(tag)`** — тег блока по умолчанию для запросов состояния (`get_balance`, `call`, `get_code`, ...). Принимает всё, что конвертируется в `BlockNumberOrTag`. По умолчанию `Latest`.

**`.retry(policy)`** — управляет повторами при временных ошибках (5xx, 429, обрывы соединения, код ошибки JSON-RPC -32005) с экспоненциальным backoff перед переключением на следующий эндпоинт. Ответы 429 с заголовком `Retry-After` учитываются.

**`.timeout(duration)`** — HTTP-таймаут каждого отдельного запроса.

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

| Метод                                                        | Возвращает                                     |
| ------------------------------------------------------------ | ---------------------------------------------- |
| `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<BlockNumberOrTag>, full_txs)` | `Option<Block>`                                |
| `get_transaction_by_hash(B256)`                              | `Option<Transaction>`                          |
| `get_transaction_receipt(B256)`                              | `Option<TransactionReceipt>`                   |
| `get_logs(&Filter)`                                          | `Vec<Log>`                                     |
| `get_code(Address)`                                          | `Bytes`                                        |
| `call(&TransactionRequest)`                                  | `Bytes`                                        |
| `estimate_gas(&TransactionRequest)`                          | `u64`                                          |
| `send_raw_transaction(&[u8])`                                | `B256` — хеш транзакции                        |
| `fee_history(blocks, newest, percentiles)`                   | `FeeHistory`                                   |
| `request(method, params)`                                    | Любой RPC-метод по имени (`serde_json::Value`) |
| `request_raw(body)`                                          | Сырое тело JSON-RPC строкой                    |

### Чтение состояния сети

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

### Фильтры логов

`get_logs` принимает `Filter` из alloy напрямую:

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

### Вызовы контрактов и транзакции

`call` и `estimate_gas` принимают `TransactionRequest` из alloy. Чтобы отправить транзакцию, соберите и подпишите её с помощью alloy (`alloy-signer`, `alloy-network`), затем разошлите через SDK:

```rust theme={null}
let hash = client.send_raw_transaction(&signed_tx_rlp).await?;
let receipt = client.get_transaction_receipt(hash).await?;
```

### Произвольные методы

`request` вызывает любой RPC-метод по имени — SDK собирает JSON-RPC-обёртку, применяет retry и failover и возвращает поле `result`. Это покрывает и методы-расширения `arb_*`, которые Robinhood Chain обслуживает как сеть на Arbitrum Nitro. `request_raw` отправляет сырое тело JSON-RPC строкой.

```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-клиент

Включите фичу `ws`. Robinhood Chain производит блоки примерно каждые 100 миллисекунд, поэтому `newHeads` срабатывает значительно чаще, чем в мейннете Ethereum и большинстве L2.

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

Минимально:

```rust theme={null}
let client = WsClientBuilder::new().build().await?;
```

Обратите внимание: `.build()` асинхронный — перед возвратом устанавливается WebSocket-соединение. Методы `.urls()`, `.fallback_url(s)()`, `.api_key()` и `.retry()` у билдера общие с RPC-билдером; специфичные для WebSocket опции:

**`.url(url)`** — основной WebSocket-эндпоинт. Порядок разрешения: `.url()`, затем `ORBITFLARE_ROBINHOOD_WS_URL`, затем значение по умолчанию `wss://robinhood.rpc.orbitflare.com` (`ws::DEFAULT_WS_URL`).

**`.ping_interval_secs(n)`** — как часто SDK отправляет фреймы WebSocket `Ping` для обнаружения мёртвых соединений. По умолчанию: 10.

**`.max_missed_pongs(n)`** — сколько ping без ответа перед тем, как соединение считается мёртвым и переподключается. По умолчанию: 3.

### Подписки

Подписки типизированы — каждая выдаёт соответствующий тип alloy вместо сырого JSON:

| Метод                                  | Выдаёт                         |
| -------------------------------------- | ------------------------------ |
| `new_heads_subscribe()`                | `Header` на каждый новый блок  |
| `logs_subscribe(&Filter)`              | `Log`, соответствующий фильтру |
| `new_pending_transactions_subscribe()` | `B256` — хеш транзакции        |

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

Все подписки возвращают `WsSubscription<T>`. Вызовите `.next()` для следующего типизированного события (`None` — подписка закрыта) или `.next_raw()` для нетипизированного `serde_json::Value`.

```rust theme={null}
while let Some(head) = heads.next().await {
    println!("block {} (gas used {})", head.number, head.gas_used);
}
```

Все подписки идут по одному WebSocket-соединению, и новые можно добавлять в любой момент. `sub.unsubscribe().await` удаляет подписку явно; если дропнуть подписку без вызова, тоже сработает — SDK обнаружит «сироту» и сам отправит отписку.

### Переподключение

Если соединение обрывается, фоновая задача переподключается с экспоненциальным backoff и автоматически заново оформляет все активные подписки. Ваши вызовы `.next()` продолжают работать — события возобновятся после восстановления связи. Мёртвые соединения обнаруживаются активным ping/pong, настраивается через `.ping_interval_secs()` и `.max_missed_pongs()`.

## Переменные окружения

| Переменная                     | Кем используется | Назначение                                                    |
| ------------------------------ | ---------------- | ------------------------------------------------------------- |
| `ORBITFLARE_LICENSE_KEY`       | RPC, WebSocket   | API-ключ, подставляемый в URL эндпоинтов                      |
| `ORBITFLARE_ROBINHOOD_RPC_URL` | RPC              | Переопределяет эндпоинт по умолчанию, если `.url()` не вызван |
| `ORBITFLARE_ROBINHOOD_WS_URL`  | WebSocket        | Переопределяет эндпоинт по умолчанию, если `.url()` не вызван |

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

Скрипт мониторинга: читает кошелёк, затем следит за новыми блоками и ERC-20-переводами в реальном времени:

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

## Исходный код

SDK с открытым исходным кодом: [github.com/orbitflare/orbitflare-robinhood-sdk-rs](https://github.com/orbitflare/orbitflare-robinhood-sdk-rs)
