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

# EVM SDK

> Rust SDK для EVM-сетей OrbitFlare, крейт orbitflare-evm-sdk.

Единый Rust-клиент для EVM-сетей OrbitFlare - Polygon, BNB Smart Chain и Robinhood Chain - плюс любая EVM-сеть, которую вы определите сами. Повсюду построен на типах [alloy](https://github.com/alloy-rs/alloy) (`Address`, `U256`, `B256`, `Filter`, `TransactionRequest`, типизированные блоки, транзакции, квитанции и логи), с собственным транспортом OrbitFlare: failover эндпоинтов, повторные попытки с backoff и самовосстанавливающиеся WebSocket-подписки.

Клиенты обобщены по сети (`RpcClient<C>`, `WsClient<C>`); приведённые ниже алиасы - это удобные сокращения. gRPC пока доступен только для Polygon (Bor).

## Поддерживаемые сети

| Сеть             | Chain ID | Алиас                                     |
| ---------------- | -------- | ----------------------------------------- |
| Polygon          | 137      | `PolygonRpcClient`, `PolygonWsClient`     |
| BNB Smart Chain  | 56       | `BnbRpcClient`, `BnbWsClient`             |
| Robinhood Chain  | 4663     | `RobinhoodRpcClient`, `RobinhoodWsClient` |
| Ваша собственная | любой    | `RpcClient<C>`, где `impl Chain for C`    |

<Note>
  Переходите с `orbitflare-robinhood-sdk`? Robinhood Chain теперь покрывается здесь через `RobinhoodRpcClient` / `RobinhoodWsClient`. Поверхность RPC и WebSocket та же самая; отдельный крейт устарел.
</Note>

## Установка

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

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

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

<CodeGroup>
  ```rust Пример 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 Полный пример 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(())
  }
  ```
</CodeGroup>

Встроенных эндпоинтов по умолчанию нет: задайте URL через `.url()` или переменную окружения (см. [Эндпоинты](#endpoints)), а API-ключ - через `.api_key()` или `ORBITFLARE_LICENSE_KEY`.

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

**`.url(url)`** - основной эндпоинт. Порядок разрешения: `.url()` в билдере, затем переменная окружения `ORBITFLARE_RPC_URL`.

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

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

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

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

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

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

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

| Метод                                                        | Возвращает                                     |
| ------------------------------------------------------------ | ---------------------------------------------- |
| `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_block_by_hash(B256, 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`                                        |
| `get_storage_at(Address, B256)`                              | `B256`                                         |
| `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 строкой                    |

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

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

<CodeGroup>
  ```rust Пример 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 Полный пример 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(())
  }
  ```
</CodeGroup>

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

`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`. `request_raw` отправляет сырое тело JSON-RPC строкой.

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

let syncing = client.request("eth_syncing", json!([])).await?;
```

## WebSocket

Включите фичу `ws`. `.build()` асинхронный - соединение устанавливается перед возвратом.

<CodeGroup>
  ```rust Пример 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 Полный пример 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(())
  }
  ```
</CodeGroup>

Билдер разделяет `.urls()`, `.fallback_url(s)`, `.api_key()` и `.retry()` с RPC-билдером; специфичные для WebSocket опции - это `.ping_interval_secs(n)` (по умолчанию 10) и `.max_missed_pongs(n)` (по умолчанию 3).

### Подписки

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

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

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

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

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

## Polygon gRPC

Polygon предоставляет gRPC-интерфейс Bor для низкозатратного доступа к блокам, заголовкам и квитанциям. Включите фичу `grpc`. gRPC работает поверх plaintext HTTP/2; аутентификация выполняется токеном через `.api_key()` (отправляется как `x-token`) либо через IP-whitelisting.

<CodeGroup>
  ```rust Пример 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 Полный пример 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(())
  }
  ```
</CodeGroup>

| Метод                                      | Возвращает                  |
| ------------------------------------------ | --------------------------- |
| `header_by_number(impl Into<BlockNumber>)` | `Header`                    |
| `block_by_number(impl Into<BlockNumber>)`  | `Block`                     |
| `transaction_receipt(B256)`                | `Receipt`                   |
| `bor_block_receipt(B256)`                  | `Receipt`                   |
| `author(impl Into<BlockNumber>)`           | `Address`                   |
| `td_by_hash(B256)` / `td_by_number(...)`   | `u64` - суммарная сложность |
| `root_hash(start, end)`                    | `String`                    |
| `block_info_in_batch(start, end)`          | `Vec<BlockInfo>`            |

Значения H160/H256 конвертируются в alloy `Address`/`B256` через трейт `ToAlloy`.

## Пользовательские сети

Работает любая EVM-сеть: реализуйте `Chain` для маркерного типа и направьте обобщённый `RpcClient<C>` на её URL. `CHAIN_ID` - это метаданные (доступны как `RpcClient::<C>::chain_id_const()`); они не ограничивают запросы.

<CodeGroup>
  ```rust Пример theme={null}
  struct Base;

  impl Chain for Base {
      const CHAIN_ID: u64 = 8453;
  }

  let client = RpcClient::<Base>::builder()
      .url("https://mainnet.base.org")
      .build()?;
  ```

  ```rust Полный пример 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::<Base>::builder()
          .url("https://mainnet.base.org")
          .build()?;

      println!("chain id: {}", client.get_chain_id().await?);
      println!("block:    {}", client.get_block_number().await?);
      Ok(())
  }
  ```
</CodeGroup>

## Поля блоков Arbitrum и Nitro

Robinhood Chain - это Arbitrum Nitro, поэтому её блоки несут дополнительные поля (`l1BlockNumber`, `sendRoot`, `sendCount`), которые стандартные типы блоков EVM отбрасывают. SDK сохраняет их и предоставляет типизированные аксессоры через `NitroBlockExt`. Любое другое нестандартное поле по-прежнему доступно в `block.other`.

<CodeGroup>
  ```rust Пример 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 Полный пример 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(())
  }
  ```
</CodeGroup>

Прекомпилы Arbitrum (ArbSys, ArbGasInfo, ...) доступны через `call()`, как и любой контракт.

## Эндпоинты

Эндпоинтов по умолчанию нет. Задайте URL для каждого клиента через `.url()` или через переменную окружения. Порядок разрешения: `.url()` в билдере, затем переменная окружения.

| Сеть            | 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` |

Используйте точные эндпоинты из вашей панели OrbitFlare.

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

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

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

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