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

# Apex Rust 客户端

> apex-sender-client 是 OrbitFlare Apex 的 Rust crate：支持 0-RTT 重连的持久 QUIC 连接，以及用于 JSON-RPC 和批量发送的 HTTP 辅助工具。

`apex-sender-client` 是 Apex 的参考客户端。它为每个 Apex 端点保持一个持久 QUIC 连接，使用由您的 API 密钥派生的客户端证书进行身份验证，每个流发送一笔序列化后的交易，并在连接断开时以 0-RTT 重连。

## 安装

该 crate **即将发布到 crates.io**，名称为 `apex-sender-client`。在发布之前，请从 OrbitFlare 的 GitHub 仓库安装：

```toml theme={null}
[dependencies]
apex-sender-client = { git = "https://github.com/orbitflare/apex-sender-client", features = ["rpc"] }
```

发布到 crates.io 之后：

```toml theme={null}
[dependencies]
apex-sender-client = { version = "0.1", features = ["rpc"] }
```

`rpc` 特性会添加 HTTP 辅助工具：`getTipAccounts`、JSON-RPC `sendTransaction`、二进制路由，以及适用于任意 Solana RPC 的 blockhash 和确认辅助工具。单独的 QUIC 路径没有 HTTP 依赖，因此如果您只需要 QUIC，请不要启用该特性。

该 crate 需要 Rust 1.92 或更高版本。

## 快速开始

```rust theme={null}
use std::time::Duration;

use apex_sender_client::rpc::{RpcClient, SolanaRpc};
use apex_sender_client::{tip, tip_instruction, ApexSenderClient, Region, MIN_TIP_LAMPORTS};
use solana_signer::Signer;
use solana_transaction::versioned::VersionedTransaction;
use solana_transaction::Transaction;

let client = ApexSenderClient::connect(Region::Frankfurt, &api_key).await?;
let tip_accounts = RpcClient::new(Region::Frankfurt, &api_key).get_tip_accounts().await?;
let tip_account = tip::pick_tip_account(&tip_accounts).ok_or("no tip accounts")?;

// Your instructions, plus the tip as a top-level instruction.
let instructions = [
    your_instruction,
    tip_instruction(&payer.pubkey(), &tip_account, MIN_TIP_LAMPORTS),
];
let solana = SolanaRpc::new(solana_rpc_url);
let blockhash = solana.latest_blockhash().await?;
let tx = VersionedTransaction::from(Transaction::new_signed_with_payer(
    &instructions,
    Some(&payer.pubkey()),
    &[&payer],
    blockhash,
));

let signature = client.send_transaction(&tx).await?; // microseconds, no acknowledgement
let slot = solana.confirm(&signature.to_string(), Duration::from_secs(30)).await?;
```

带有 `Cargo.toml` 的完整程序，参见[快速入门](/cn/apex/quickstart)的 Rust 标签页。

## 区域

`Region` 为每个 Apex 端点命名。`Region::parse("fra")` 和 `Region::code()` 用于与短代码相互转换，`Region::ALL` 列出全部区域。

| 变体                       | 代码       |
| ------------------------ | -------- |
| `Region::Frankfurt`      | `fra`    |
| `Region::Amsterdam`      | `ams`    |
| `Region::London`         | `lon`    |
| `Region::NewYork`        | `nyc`    |
| `Region::SaltLakeCity`   | `slc`    |
| `Region::Singapore`      | `sgp`    |
| `Region::Tokyo`          | `tyo`    |
| `Region::Siauliai`       | `sqq`    |
| `Region::Global`（正在逐步上线） | `global` |

`region.quic_endpoint()` 返回 `<code>.apex.orbitflare.com:7001`，`region.rpc_url()` 返回 `http://<code>.apex.orbitflare.com`。参见[端点与区域](/cn/apex/endpoints)。

## 传输方式

|             | QUIC 单向                                      | QUIC 双向                                                | HTTP 二进制                                                | JSON-RPC                           |
| ----------- | -------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------- | ---------------------------------- |
| **调用**      | `send_transaction`, `send_transaction_bytes` | `send_transaction_with_response`, `send_with_response` | `rpc::RpcClient::send_transaction_binary`, `send_batch` | `rpc::RpcClient::send_transaction` |
| **返回**      | 签名。不读取任何返回内容                                 | 已接受，或拒绝代码和消息                                           | 签名，或带标签的错误                                              | 签名，或 JSON-RPC 错误                   |
| **每次发送的开销** | 预热连接上的一个流                                    | 一个流加一次往返                                               | 一个携带原始字节的 HTTP 请求                                       | 一个在 JSON 中携带 base64 的 HTTP 请求      |
| **最适合**     | 使用持久连接的机器人                                   | 集成、调试，以及希望直接获得原因的工具                                    | 批量发送                                                    | 直接替换 `sendTransaction` 代码          |

传输方式不会改变优先级或路由。起作用的是小费。

## API

| 项                                                                                 | 描述                                                                                                                |
| --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `ApexSenderClient::connect(region, api_key)`                                      | 使用临时本地端口和默认选项连接                                                                                                   |
| `ApexSenderClient::connect_with_options(opts, api_key)`                           | 使用 [`ClientOptions`](#选项) 连接                                                                                      |
| `send_transaction(&tx)`                                                           | 序列化（legacy、v0 或 v1）并在单向流上发送。返回第一个签名                                                                               |
| `send_transaction_bytes(bytes)`                                                   | 同上，但使用您已持有的字节。不会对其重新编码                                                                                            |
| `send_transaction_with_response(&tx)`                                             | 双向流。被拒绝时返回 `Err(Error::Rejected { code, message })`                                                               |
| `send_with_response(bytes)`                                                       | 同上，但使用字节。返回原始的 `Admission`                                                                                        |
| `health()`, `reconnects_total()`, `zero_rtt_resumptions_total()`, `remote_addr()` | 连接状态，用于您的指标                                                                                                       |
| `reconnect()`, `close()`                                                          | 生命周期                                                                                                              |
| `tip_instruction(payer, tip_account, lamports)`                                   | 构建小费转账                                                                                                            |
| `tip::pick_tip_account(&accounts)`                                                | 随机选择一个小费账户                                                                                                        |
| `MIN_TIP_LAMPORTS`                                                                | 标准等级下限：1,000,000 lamports                                                                                         |
| `rpc::RpcClient`                                                                  | 通过 HTTP 访问 Apex 端点：`get_tip_accounts`、`send_transaction` (JSON-RPC)、`send_transaction_binary`、`send_batch`、`ping` |
| `rpc::SolanaRpc`                                                                  | 任意 Solana RPC：`latest_blockhash`、`confirm(signature, timeout)`                                                    |
| `rpc::fetch_vaults(solana_rpc_url)`                                               | 直接从链上小费程序（`rpc::TIP_PROGRAM_ID`）读取小费账户，无需调用 Apex 端点                                                               |
| `client_pubkey(api_key)`                                                          | 您的 API 密钥派生出的证书密钥，与控制台上显示的一致                                                                                      |
| `serialize_transaction(&tx)`                                                      | 规范的线路字节。对 legacy 和 v0 与 bincode 相同，对 [v1](/cn/apex/transaction-v1) 也正确                                            |
| `wire::encode_packet`, `wire::decode_admission`                                   | QUIC 线路格式，可作为其他语言的参考                                                                                              |

客户端的 `Clone` 开销很低，克隆出的副本共享同一个连接。创建一次，然后将克隆分发给您的各个任务。

<Note>
  该 crate 目前还没有捆绑包辅助工具。请通过 HTTP 使用 `sendBundle` 或 `POST /send-bundle` 发送[捆绑包](/cn/apex/bundles)。
</Note>

## 选项

`ClientOptions` 实现了 `Default`，因此只需设置您需要的字段：

| 字段                    | 默认值          | 描述                                                      |
| --------------------- | ------------ | ------------------------------------------------------- |
| `endpoint`            | 该区域的 QUIC 地址 | QUIC 地址的 `host:port`。会覆盖区域设置                            |
| `mev_protect`         | `false`      | 跳过 Shield 黑名单上的领导者。参见 [MEV 保护](/cn/apex/mev-protection) |
| `max_retries`         | `None`       | 每笔交易的重试预算。`None` 使用端点的默认值                               |
| `bind_addr`           | 临时端口         | 本地 UDP 绑定地址，用于防火墙白名单                                    |
| `connect_timeout`     | 3 s          | 握手超时                                                    |
| `send_timeout`        | 2 s          | 单次发送的超时                                                 |
| `keep_alive`          | 1 s          | QUIC PING 间隔。端点的空闲超时为 30 s                              |
| `auto_reconnect`      | `true`       | 当发送因连接已断开而失败时，重新连接并再发送一次                                |
| `proactive_reconnect` | `true`       | 后台任务在发现断线后立即重新握手，这样下一次发送就不必为此付出代价                       |

```rust theme={null}
use std::time::Duration;

use apex_sender_client::{ApexSenderClient, ClientOptions, Region};

let client = ApexSenderClient::connect_with_options(
    ClientOptions {
        endpoint: Some(Region::NewYork.quic_endpoint()),
        bind_addr: Some("0.0.0.0:47001".parse()?),
        mev_protect: true,
        max_retries: Some(30),
        send_timeout: Duration::from_secs(1),
        ..Default::default()
    },
    &api_key,
)
.await?;
```

## 保活与重连

连接通过每秒一次的 QUIC PING 保持打开，而 Apex 端点的空闲超时为 30 秒。在预热的连接上，一次发送就是打开一个流并写入一次。

如果连接断开：

* 后台任务会发现并立即重新握手（`proactive_reconnect`），因此下一次发送通常能找到一个可用的连接。
* 重连会使用缓存的会话票据，并在握手的首批数据包中发送等待中的交易（**0-RTT**）。如果端点拒绝早期数据，客户端会在握手完成后重新发送。
* 因连接已断开而失败的发送会重新连接并重试一次（`auto_reconnect`）。将其设为 `false` 可自行处理这种情况。

`health()`、`reconnects_total()` 和 `zero_rtt_resumptions_total()` 会显示发生了什么。请将它们导出到您的指标中。

## 错误

| `Error` 变体                                                         | 含义                                                             |
| ------------------------------------------------------------------ | -------------------------------------------------------------- |
| `Rejected { code, message }`                                       | 端点在双向流上拒绝了交易。参见[准入码](/cn/apex/errors-and-rate-limits#quic-准入码) |
| `TooLarge(bytes)`                                                  | 超过 4096 字节。在发送任何内容之前于本地捕获                                      |
| `Timeout`                                                          | 连接或发送超时已到                                                      |
| `Resolve`, `Bind`, `Tls`, `Connect`, `Connection`, `Write`, `Read` | 传输失败                                                           |
| `Serialize`, `BadAdmission`                                        | 编码问题                                                           |
| `Closed`                                                           | 客户端已关闭                                                         |

HTTP 辅助工具返回 `rpc::RpcError`：`Http` 表示传输失败，`Rpc { code, message }` 表示 JSON-RPC 或普通 HTTP 拒绝，另有 `BadResponse`。

## 仓库中的示例

所有示例都读取 `APEX_API_KEY`、`KEYPAIR_PATH`（默认 `payer.json`）、`SOLANA_RPC_URL`，以及可选的 `APEX_REGION`、`APEX_QUIC`、`APEX_RPC`、`TIP_LAMPORTS` 和 `APEX_TX_VERSION`（`legacy` 或 `v1`）。每个示例都会发送一笔带小费的 memo 交易，并报告其上链的 slot。

| 示例                        | 展示内容                                         |
| ------------------------- | -------------------------------------------- |
| `quic_send`               | 单向流：最快的路径，然后通过 Solana RPC 确认                 |
| `quic_send_with_response` | 双向流：已接受或被拒绝的应答，以及如何解读拒绝                      |
| `rpc_send`                | 通过 HTTP 使用 JSON-RPC `sendTransaction`，小费规则相同 |
| `raw_bytes`               | 线路上的预序列化字节，以及供其他语言参考的精确数据包布局                 |
| `throughput`              | 一个预热连接，N 个并发发送，每次发送的 p50 和 p99，以及上链数量        |
| `client_pubkey`           | API 密钥派生出的证书密钥，用于与您的控制台进行比较                  |
| `typescript/send_rpc.ts`  | 使用 `@solana/web3.js` 的 JSON-RPC 路径，无需客户端库    |
| `python/send.py`          | 在 Python 中使用 `solders` 调用二进制路由和 JSON-RPC     |

```bash theme={null}
APEX_API_KEY=... KEYPAIR_PATH=payer.json SOLANA_RPC_URL=https://... \
  cargo run --release --example quic_send --features rpc
```

主网上的典型输出，来自一个紧邻 Apex 端点的客户端：

```text theme={null}
sent in 16 us
landed in slot 447831391 after 745 ms: 5gwAmfVM...
```

## 客户端不做的事

* 构建或签名交易，或为您选择优先费。
* 模拟或运行预检。在您和领导者之间，没有任何环节会这样做。
