> For the complete documentation index, see [llms.txt](https://docs.termina.technology/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.termina.technology/documentation/developer-docs-zh/gai-shu/an-zhuang/rust-can-kao.md).

# Rust 参考

## Rust 参考

`simulator-client` 和 `simulator-api` 这两个 crate 为 Termina 模拟器提供原生 Rust 接口，适合希望将回测直接集成到 Rust 代码中、而不是使用 `sim` CLI 的团队。

* **`simulator-client`** 是高层异步客户端。它封装了 WebSocket 协议，并为常见工作流提供易用的构建器：创建会话、推进 slot、注入交易以及读取账户状态。
* **`simulator-api`** 定义了原始协议类型（请求/响应结构体、错误变体、会话参数）。如果需要直接访问传输格式，或想实现自己的客户端，可以使用它。

完整的端到端示例请参阅入门代码[仓库](https://github.com/nitro-svm/examples)。

#### 安装

```toml
[dependencies]
simulator-client = "0.23"
simulator-api = "0.23" # 仅在需要协议类型时添加（例如构建自定义客户端）
```

#### 示例

**可用 slot**

创建会话之前，请先确认要回测的 slot 范围可用。

```rust
use simulator_client::BacktestClient;

let client = BacktestClient::builder()
    .url("wss://simulator.termina.technology/backtest")
    .api_key(api_key)
    .build();

let ranges = client.available_ranges().await?;
for r in &ranges {
    println!(
        "slots {} – {}",
        r.bundle_start_slot,
        r.max_bundle_end_slot.unwrap_or(0)
    );
}
```

**会话初始化**

在会话创建参数中设置 slot 范围及可选字段，然后创建会话：

* `signer_filter`：跳过由这些地址签名的历史交易
* `actions`：在每个 slot 或由特定交易触发时，执行预先设定的操作
* `extra_compute_units`：提高计算单元上限，避免交易失败

其他字段见[完整参考](https://docs.rs/simulator-api/0.23.0/simulator_api/struct.CreateSessionParams.html)。

```rust
use simulator_client::{Continue, CreateSession, ManagedBacktestSession, ManagedEvent};

let request = CreateSession::builder()
    .start_slot(300_000_000)
    .slot_count(100)
    .build();
```

```rust
// 使用托管客户端而非原始的 `BacktestClient`，
// 因为它会在连接断开时自动重试
let mut session = ManagedBacktestSession::start(
    "wss://simulator.termina.technology/backtest".to_string(),
    api_key,
    request,
).await?;

loop {
    // 服务器会先针对 `startSlot` 发出一条 `slotNotification`，
    // 会话就绪后再发出 `readyForContinue`
    match session.next_event().await? {
        ManagedEvent::ReadyForContinue => {
            session.send_continue(Continue::builder().build().into_params()).await?;
        }
        ManagedEvent::Completed { .. } => break,
        _ => {}
    }
}
session.shutdown().await;
```

**读取与写入**

每个会话都在 `/backtest/<session_id>` 上支持标准的 Solana JSON-RPC 方法和订阅，包括：

* `getAccountInfo`、`getBalance`、`getMultipleAccounts`
* `accountSubscribe`、`programSubscribe`、`signatureSubscribe`

> 完整的 Solana 订阅方法列表见 [API 参考](https://claude.ai/documentation/overview/installation/api-reference.md#api-table)。

使用会话的 RPC 客户端发送交易和读取账户。

```rust
use std::str::FromStr;
use solana_sdk::pubkey::Pubkey;

// 使用任意 Solana SDK 工具构建交易
let tx: solana_sdk::transaction::VersionedTransaction = /* ... */;

session
    .continue_until_ready(
        Continue::builder()
            .advance_count(1)
            .build()
            .push_transaction(&tx)?,
        Some(Duration::from_secs(30)),
        |_| {},
    )
    .await?;

let pubkey = Pubkey::from_str("SomePubkey11111111111111111111111111111111")?;
let account = session.rpc().get_account(&pubkey).await?;
println!("lamports: {}", account.lamports);
```

也可以建立 WebSocket 连接，使用标准订阅方法。

```rust
use solana_commitment_config::CommitmentConfig;

let _handle = session
    .subscribe_program_logs(
        "YourProgramId111111111111111111111111111111",
        CommitmentConfig::confirmed(),
        |notification| async move {
            println!("logs: {:?}", notification.value.logs);
        },
    )
    .await?;

let _handle = session
    .subscribe_account_diffs(
        "SomePubkey11111111111111111111111111111111",
        |diff| async move {
            println!("account changed: {:?}", diff);
        },
    )
    .await?;

// 丢弃 handle 即可取消订阅
```

**程序替换**

编译新的二进制文件并替换现有程序，以测试新的程序逻辑。

```rust
let elf = std::fs::read("your_program.so")?;

// 通过会话的 RPC 端点推导正确的 ProgramData 账户结构
let modifications = session
    .modify_program("YourProgramId111111111111111111111111111111", &elf)
    .await?;

// 在下一次 `Continue` 时应用这些修改
session
    .continue_until_ready(
        Continue::builder()
            .advance_count(1)
            .modify_accounts(modifications)
            .build(),
        None,
        |_| {},
    )
    .await?;
```

**重新路由订单流**

通过 Jupiter Metis 重新路由历史 swap，观察参数变化会如何影响某个池子本可以捕获的订单流。

```rust
use simulator_client::{AccountModifications, CreateSession};

// 如有需要，可以注入一个链上尚不存在的池子，以测试新市场的报价
// 最简单的方法是复制并修改同一程序下的现有池子
let accounts = AccountModifications(BTreeMap::from([
    (pool, AccountData {
        data: EncodedBinary::new(pool_data_b64, BinaryEncoding::Base64),
        owner: pool_program,
        lamports: pool_lamports,
        executable: false,
        space: pool_data_len,
    }),
    (vault_a, /* 持有库存的 SPL 代币账户 */),
    (vault_b, /* 持有库存的 SPL 代币账户 */),
]));
```

{% hint style="warning" %} 注入新池子时，如果程序会校验账户之间的关系（例如由池子和 mint 派生的 vault），需要保持这些关系一致，否则经由新池子的 swap 会失败。 {% endhint %}

更新会话创建参数以启用重新路由。

```rust
use simulator_api::{RerouteAggregators, SwapAggregator};

let all_aggregators = RerouteAggregators::new([
    SwapAggregator::Jupiter,
    SwapAggregator::Okx,
    SwapAggregator::Dflow,
    SwapAggregator::Titan,
]);

let request = CreateSession::builder()
    .start_slot(START_SLOT)
    .end_slot(END_SLOT)
    .reroute_order_flow(true)               // 通过 Jupiter Metis 重新路由历史交易
    .reroute_aggregators(all_aggregators)   // 筛选要重新路由哪些聚合器的交易
    .reroute_extra_markets([pool].into())   // 让路由器识别该池子
    .build()
    .add_override(START_SLOT, accounts)     // 从第一个 slot 起让该池子存在
    .into_request()?;
```

**汇总与测量**

要获取重新路由的汇总，请订阅 `replacementSubscribe` 通知。每笔重新路由的 swap 包含：

* 路由器选择的路径（逐跳列出），以及每一跳所用池子的地址
* 每一跳的输入和输出数量
* 链上的原始路径和成交结果，用于对比

```rust
use simulator_client::reroute_report::{self, Target};

// 通过路由器标签、程序 ID 或两者来识别交易场所
let target = Target::new(None, Some(pool_program));
let report = reroute_report::from_notifications(target, &notifications)?;
println!("{}", report.render(None));
```

会话汇总还会报告检测到、重新路由、模拟以及成功的 swap 数量。
