# Agent 程序化充值：使用 x-api-key 自动链上充值

> 原文地址: https://docs.blockvectra.com/zh/guides/agent-topup/

面向自主 AI Agent、自动化脚本与后端服务器程序，BlockVectra 提供基于 HTTP 接口的程序化充值流程。无需浏览器介入，程序可通过已有的 API key 获取账户专属的 EVM 充值地址、检查开放网络与代币，并在链上转账后轮询到账状态。

> **API key 安全与调用环境限制**
>
> `x-api-key` 请求头**只能在服务器端调用**。出于安全考虑，浏览器跨域预检有意不放行该请求头，请勿在前端浏览器代码中调用充值接口，切勿将 API key 写入前端代码、公开仓库或 AI 对话中。


## 适用场景

* **自主 AI Agent**：当计算单元（Compute Units，CU）或余额不足时，Agent 自动检查充值通道并自主完成链上补充。
* **CI/CD 与自动化运维**：测试流水线与定时任务服务器按需维持账户可用余额。
* **无前端参与的后台服务**：纯服务端程序直接通过标准 HTTP 客户端管理充值流程。

## 前提条件

* **已有 API key**：调用充值接口需要已激活的 BlockVectra RPC API key。若没有 key，可参考[程序化开户指南](https://docs.blockvectra.com/zh/guides/programmatic-signup/)使用钱包签名程序化开户并建 key，也可以在[控制台](https://console.blockvectra.com/zh/login/?next=%2Fzh%2Fkeys%2F)创建。
* **链上资产**：程序运行环境或关联钱包中持有支持网络上的 USDT 或 USDC，并有足够的网络原生代币支付 Gas 费。
* **环境变量**：建议将 API key 注入为环境变量 `BLOCKVECTRA_API_KEY`。

带鉴权的充值接口直接接受 `x-api-key` 请求头，使用调用 RPC 所用的同一个 API key 即可，不需要浏览器。

## 四步充值流程

接口统一使用生产主机：

```
https://api.blockvectra.com
```

### 1. 检查充值可用性（GET /v1/topup/status）

发起转账前，应先确认充值通道的全局开关与目标网络是否开放。该接口为公开接口，无需凭据。

```bash
curl -s https://api.blockvectra.com/v1/topup/status
```

响应示例：

```json
{
  "enabled": true,
  "networks": [
    {
      "network": "base_mainnet",
      "chain_id": 8453,
      "token": "USDC",
      "enabled": true
    },
    {
      "network": "bsc_mainnet",
      "chain_id": 56,
      "token": "USDT",
      "enabled": true
    },
    {
      "network": "bsc_mainnet",
      "chain_id": 56,
      "token": "USDC",
      "enabled": true
    }
  ],
  "min_deposit_usd": "1.000000"
}
```

* `enabled`：全局总开关。若为 `false`，表示所有网络充值均已关闭。
* `networks`：各网络与代币的开启状态。当某个网络或代币的 `enabled` 为 `false` 时，**切勿向该网络转账**。
* `min_deposit_usd`：全局最低充值金额（美元，保留 6 位小数）。

### 2. 获取专属充值地址与开放网络（GET /v1/topup/deposit-address）

获取当前账户对应的专属 EVM 充值地址以及当前开放的网络和代币合约。该接口需要 `x-api-key` 鉴权，且仅支持在服务器端调用。

```bash
curl -s \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" \
  https://api.blockvectra.com/v1/topup/deposit-address
```

响应示例：

```json
{
  "address": "0x<your-dedicated-deposit-address>",
  "min_deposit_usd": "1.000000",
  "deposits_url": "https://api.blockvectra.com/v1/topup/deposits",
  "networks": [
    {
      "chain": "base_mainnet",
      "chain_id": 8453,
      "name": "Base",
      "typical_credit_seconds": 30,
      "explorer_tx_url": "https://basescan.org/tx/{tx_hash}",
      "tokens": [
        {
          "symbol": "USDC",
          "contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
          "decimals": 6,
          "min_amount_raw": "1000000"
        }
      ]
    },
    {
      "chain": "bsc_mainnet",
      "chain_id": 56,
      "name": "BNB Smart Chain",
      "typical_credit_seconds": 60,
      "explorer_tx_url": "https://bscscan.com/tx/{tx_hash}",
      "tokens": [
        {
          "symbol": "USDT",
          "contract": "0x55d398326f99059fF775485246999027B3197955",
          "decimals": 18,
          "min_amount_raw": "1000000000000000000"
        },
        {
          "symbol": "USDC",
          "contract": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
          "decimals": 18,
          "min_amount_raw": "1000000000000000000"
        }
      ]
    }
  ]
}
```

* `address`：EIP-55 校验和格式的 EVM 专属充值地址。
* `networks`：当前开放的网络列表，未开放的网络不会出现在列表中。包含链标识 `chain`、EVM 链 ID `chain_id`、网络名称 `name`、包含交易后的典型到账参考秒数 `typical_credit_seconds`、浏览器交易链接模板 `explorer_tx_url`。
* `tokens`：该网络支持的稳定币列表，包含代币符号 `symbol`（USDT 或 USDC）、合约地址 `contract`、代币精度 `decimals`，以及原始最小转账数额 `min_amount_raw`。

> **代币精度与金额换算**
>
> 同一代币在不同链上的精度不同（例如 BSC 上的 USDT 与 USDC 均为 18 位精度，而 Base 上的 USDC 为 6 位精度）。金额换算必须以该网络返回的 `decimals` 字段为准，切勿写死单一代币精度。


#### 错误响应说明

带鉴权的充值接口（`/v1/topup/deposit-address` 与 `/v1/topup/deposits`）常见错误响应：

* **HTTP 401（鉴权失败）**：未提供 `x-api-key` 请求头时返回 `missing_api_key`；提供的 key 无效、已停用或已吊销时返回 `invalid_api_key`：

```json
{
  "error": {
    "code": "missing_api_key",
    "message": "missing x-api-key header"
  }
}
```

* **HTTP 409（通道关闭）**：若充值通道全局关闭或所有网络均未开放，接口返回 HTTP 409，错误码为 `topup_disabled`：

```json
{
  "error": {
    "code": "topup_disabled",
    "message": "topup is disabled"
  }
}
```

在返回 409 时，系统不会分配新地址。

### 3. 发起链上转账

通过 Agent 的钱包程序或脚本，向第 2 步获取到的专属 `address` 发起 ERC-20 `transfer` 交易。

转账要求：

* 仅转账 `tokens` 列表中列出的代币合约与网络。
* 转账金额必须大于或等于 `min_amount_raw`（即 `min_deposit_usd`），并按对应网络代币的 `decimals` 精度换算。
* 转账提交后，记下链上交易哈希（`tx_hash`）。

### 4. 轮询充值记录与确认到账（GET /v1/topup/deposits）

交易在链上打包后，调用该接口查询充值记录与入账状态。该接口需要 `x-api-key` 鉴权，仅限服务器端调用。

#### 查询参数说明

* `limit`：单页返回的充值记录数量，默认 `20`，取值范围 `1`–`100`。
* `before`：按 `deposit_id` 的游标分页参数。传入上一页响应中的 `next_before` 数值即可获取下一页历史记录。
* `tx_hash`：可选的 0x 前缀 64 位十六进制交易哈希，用于精确过滤单笔转账。

支持通过 `tx_hash` 精确过滤指定交易：

```bash
curl -s \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" \
  "https://api.blockvectra.com/v1/topup/deposits?tx_hash=0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
```

响应示例：

```json
{
  "items": [
    {
      "deposit_id": 42,
      "chain": "base_mainnet",
      "chain_id": 8453,
      "token": "USDC",
      "contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "25.000000",
      "tx_hash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
      "tx_log_ordinal": 0,
      "block_number": 123456789,
      "external_ref": "eip155:8453:0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890:0",
      "status": "credited",
      "reason": null,
      "credited_units": 250000,
      "credited_cu": 250000000,
      "detected_at": "2026-10-02T10:00:00Z"
    }
  ],
  "next_before": null
}
```

* `items`：符合查询条件的充值记录列表。
* `next_before`：存在更多历史记录时的下一页游标 ID，无更多数据时为 `null`；配合 `before` 请求参数完成游标分页。

充值状态（`status`）字段取值说明：

* `processing`：已在链上检测到转账，正在等待达到所需区块确认数。
* `credited`：已成功入账并增加账户余额。此时 `credited_units` 与 `credited_cu` 记录实际增加的额度。
* `not_credited`：转账未能入账。此时 `reason` 字段会注明具体原因：
  * `below_minimum`：转账金额低于最低充值额度。
  * `large_amount`：大额充值触发人工核验。
  * `other`：其他无法入账的原因。

到账时间与轮询建议：

* **到账时间**：以第 2 步返回的 `typical_credit_seconds` 为准。
* **轮询建议**：建议轮询频率为**每 20–60 秒**一次，不要更频繁，避免触发接口频率限制。
* **状态流转**：达到链上确认数后，状态会自动从 `processing` 更新为 `credited`。

## 代码集成示例

以下展示使用 Node.js 与 Python 读取环境变量 `BLOCKVECTRA_API_KEY` 完成充值查询的完整示例。

### Node.js（fetch）

```javascript
import process from "node:process";

const apiKey = process.env.BLOCKVECTRA_API_KEY;
if (!apiKey) {
  throw new Error("缺少 BLOCKVECTRA_API_KEY 环境变量");
}

const BASE_URL = "https://api.blockvectra.com";

// 1. 检查可用性
const statusRes = await fetch(`${BASE_URL}/v1/topup/status`);
const status = await statusRes.json();
if (!status.enabled) {
  throw new Error("充值通道当前已关闭");
}

// 2. 获取专属充值地址与开放网络
const addressRes = await fetch(`${BASE_URL}/v1/topup/deposit-address`, {
  headers: { "x-api-key": apiKey },
});

if (addressRes.status === 401) {
  throw new Error("API key 缺失或无效 (HTTP 401)");
}
if (addressRes.status === 409) {
  throw new Error("充值通道已关闭 (topup_disabled)");
}
if (!addressRes.ok) {
  throw new Error(`获取充值地址失败: ${addressRes.status}`);
}

const depositData = await addressRes.json();
console.log("专属充值地址:", depositData.address);
console.log("支持网络数:", depositData.networks.length);

// 3. 轮询充值状态函数
async function checkDepositStatus(txHash) {
  const url = new URL(`${BASE_URL}/v1/topup/deposits`);
  url.searchParams.set("tx_hash", txHash);

  const res = await fetch(url, {
    headers: { "x-api-key": apiKey },
  });
  if (res.status === 401) {
    throw new Error("API key 缺失或无效 (HTTP 401)");
  }
  if (!res.ok) {
    throw new Error(`查询充值记录失败: ${res.status}`);
  }
  return res.json();
}
```

### Python（requests）

```python
# pip install requests
import os
import requests

api_key = os.environ.get("BLOCKVECTRA_API_KEY")
if not api_key:
    raise ValueError("缺少 BLOCKVECTRA_API_KEY 环境变量")

base_url = "https://api.blockvectra.com"

# 1. 检查可用性
resp = requests.get(f"{base_url}/v1/topup/status", timeout=10)
resp.raise_for_status()
status_data = resp.json()
if not status_data.get("enabled"):
    raise RuntimeError("充值通道当前已关闭")

# 2. 获取专属充值地址
resp = requests.get(
    f"{base_url}/v1/topup/deposit-address",
    headers={"x-api-key": api_key},
    timeout=10,
)
if resp.status_code == 401:
    raise RuntimeError("API key 缺失或无效 (HTTP 401)")
if resp.status_code == 409:
    raise RuntimeError("充值通道已关闭 (topup_disabled)")
resp.raise_for_status()
deposit_data = resp.json()
print("专属充值地址:", deposit_data["address"])

# 3. 轮询充值状态函数
def check_deposit_status(tx_hash: str):
    resp = requests.get(
        f"{base_url}/v1/topup/deposits",
        headers={"x-api-key": api_key},
        params={"tx_hash": tx_hash},
        timeout=10,
    )
    if resp.status_code == 401:
        raise RuntimeError("API key 缺失或无效 (HTTP 401)")
    resp.raise_for_status()
    return resp.json()
```

## 注意事项

* **网络与代币限制**：只能向 `GET /v1/topup/status` 与 `GET /v1/topup/deposit-address` 明确列出的开放网络与代币合约转账。若某网络在 `status` 中的 `enabled` 为 `false`，请勿向其转账。
* **代币精度换算**：同一代币在不同网络上的精度不同（例如 BSC 上的 USDT 与 USDC 均为 18 位精度，Base 上的 USDC 为 6 位精度），金额换算必须以接口返回的 `decimals` 为准。
* **最低充值数额**：充值数额以接口返回的 `min_deposit_usd` 与 `min_amount_raw` 为准。低于最低额度的转账不会自动入账。
* **转账不可逆**：转错链或转错代币无法自动入账。发起转账前请务必核对网络 chain\_id 与代币合约地址。
* **套餐与速率变更**：首笔充值成功到账后，账户将从免费套餐变更为付费账户，解除免费套餐的每秒调用上限；每个 key 依然受 CU 速率与突发容量约束。关于计量单位与扣费规则，请参阅[充值与计费规则](https://docs.blockvectra.com/zh/guides/billing-rules/)与[免费套餐](https://docs.blockvectra.com/zh/guides/free-plan/)。
* **轮询频率**：查询充值状态建议每 20–60 秒轮询一次，不要更频繁，避免触发接口频率限制。
* **仅限服务器端使用**：`x-api-key` 请求头切勿暴露在浏览器端或前端应用中。

## 下一步

* [查询余额（`GET /v1/account`）](https://docs.blockvectra.com/zh/guides/billing-rules/#查询余额get-v1account)，获取账户当前可用余额与计算单元（CU）额度。
* [计费规则](https://docs.blockvectra.com/zh/guides/billing-rules/)，了解计算单元（CU）计量、速率限额与不计费异常场景。
* [程序化开户指南](https://docs.blockvectra.com/zh/guides/programmatic-signup/)，使用以太坊钱包签名免浏览器自主开户与建 key。
