# 用 USDC / USDT / USDG 支付 RPC 費用：AI Agent 程式化儲值

> Source: https://docs.blockvectra.com/zh-hant/guides/agent-topup/

開發者與 AI Agent 可透過 HTTP 為 RPC 與 Data API 帳戶儲值：檢查開放的網路與代幣，使用既有的 API key 取得帳戶專屬的 EVM 儲值地址，並在轉帳後輪詢入帳狀態。在進行儲值前，請查看[定價頁面](https://blockvectra.com/en/pricing/)並[根據 CU 權重估算 RPC 與 Data API 成本](https://docs.blockvectra.com/en/guides/reading-cu-pricing/)。

## 在帳單頁取得你的儲值地址

登入後，[開啟帳單頁以取得你的儲值地址](https://console.blockvectra.com/login/?next=%2Fbilling%2F)，並使用你的帳戶所顯示的儲值地址與代幣詳情。在轉帳前，請先透過 [GET /v1/topup/status](https://api.blockvectra.com/v1/topup/status) 確認目前的網路、代幣與最低儲值門檻。

> **API key 安全與伺服器端呼叫限制**
>
> `x-api-key` 標頭**只能在伺服器端環境中呼叫**。切勿在用戶端瀏覽器程式碼中呼叫儲值端點，亦切勿在前端套件、公開儲存庫或 AI 聊天對話中暴露你的 API key。


## 前置條件

* **既有的 API key**：呼叫需要驗證的儲值端點需要已啟用的 BlockVectra RPC API key。如果你還沒有 API key，請參考[程式化註冊指南](https://docs.blockvectra.com/en/guides/programmatic-signup/)使用以太坊錢包簽名進行註冊並建立 key，或在[控制台](https://console.blockvectra.com/login/?next=%2Fkeys%2F)中建立。
* **鏈上資產**：你的 Agent 環境或資金錢包必須持有 `GET /v1/topup/status` 在支援網路上列出的 USDC / USDT / USDG，以及足夠用於廣播交易的原生 gas 代幣。
* **環境變數**：將你的 key 儲存在 `BLOCKVECTRA_API_KEY` 環境變數中。

需要驗證的儲值端點直接接受 `x-api-key` 標頭，使用與 RPC 呼叫相同的 API key 即可。無需瀏覽器工作階段。

## 四步驟儲值工作流程

一旦第一筆付費儲值入帳，免費週期補充即會停止，未使用的免費額度仍可使用，且帳戶層級的呼叫速率上限將被移除；個別 key 的速率限制保持不變。參閱[定價規則](https://blockvectra.com/en/pricing/)與[免費方案規則](https://blockvectra.com/en/free/#rules)；從 [GET /v1/plans](https://console-api.blockvectra.com/v1/plans)（`free`、`key_defaults` 與 `pricing.min_topup_usd`）讀取目前限制與最低儲值金額。

儲值端點（狀態、儲值地址與儲值記錄）使用生產環境 API 主機：

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

方案限制與定價參數由控制台 API 提供，位於 `https://console-api.blockvectra.com`（例如 [GET https://console-api.blockvectra.com/v1/plans](https://console-api.blockvectra.com/v1/plans)）。

### 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
    }
  ]
}
```

* `enabled`：全域開關。若為 `false`，則所有網路的儲值均已關閉。
* `networks`：每個網路與代幣的開放狀態。當某個網路或代幣的 `enabled` 為 `false` 時，**請勿在該網路上轉移資金**。
* `min_deposit_usd`：以 USD 表示的全域最低儲值金額，格式化為 6 位小數。最低儲值門檻是動態的：請始終參考 `GET https://api.blockvectra.com/v1/topup/status` 即時回傳的 `min_deposit_usd`。

若要直接讀取目前生效的 `min_deposit_usd`：

```bash
curl -s https://api.blockvectra.com/v1/topup/status | jq -r .min_deposit_usd
```

### 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>",
  "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 儲值地址。
* `deposits_url`：用於查詢客戶儲值記錄的 URL。
* `networks`：開放的 EVM 網路清單。已關閉的網路會被省略。包含鏈識別代號 `chain`、EVM chain ID `chain_id`、顯示名稱 `name`、區塊包含後以秒為單位的典型入帳延遲 `typical_credit_seconds`，以及區塊瀏覽器交易 URL 範本 `explorer_tx_url`。
* `tokens`：該網路上的代幣，包括代幣符號 `symbol`（USDC / USDT / USDG）、合約地址 `contract`、代幣小數位數 `decimals`，以及原始原子單位的最低儲值金額 `min_amount_raw`（請參考端點回傳的實際值；不要假定縮放後的金額）。

> **代幣小數位數與金額換算**
>
> 同一代幣在不同的鏈上可能有不同的小數位數（例如 BSC 上的 USDT 與 USDC 為 18 位小數，而 Base 上的 USDC 為 6 位小數）。金額計算必須使用該特定網路回傳的 `decimals`，而不是硬編碼單一代幣的小數值。


#### 錯誤回應

需要驗證的儲值端點（`/v1/topup/deposit-address` 與 `/v1/topup/deposits`）回傳標準 JSON 錯誤結構：

* **HTTP 401（驗證失敗）**：當缺少 `x-api-key` 標頭（`missing_api_key`）或 key 無效、已撤銷或已停用（`invalid_api_key`）時回傳：

```json
{
  "error": {
    "code": "missing_api_key",
    "message": "missing API key: send it in the x-api-key header",
    "data": {
      "reason": "missing_api_key",
      "docs_url": "https://docs.blockvectra.com/en/errors/#missing_api_key",
      "retryable": false
    }
  }
}
```

* **HTTP 409（儲值已停用）**：當全域或跨所有網路關閉儲值時（`topup_disabled`）回傳：

```json
{
  "error": {
    "code": "topup_disabled",
    "data": {
      "reason": "topup_disabled",
      "docs_url": "https://docs.blockvectra.com/en/errors/#topup_disabled",
      "retryable": false
    }
  }
}
```

完整錯誤碼清單請參閱[錯誤參考](https://docs.blockvectra.com/en/errors/)。

### 3. 廣播鏈上轉帳

使用你的 Agent 錢包或指令碼，向步驟 2 中擷取的儲值 `address` 提交 ERC-20 `transfer` 交易。

轉帳要求：

* 僅發送該網路的 `tokens` 陣列中列出的代幣與合約。
* 確保轉帳金額大於或等於 `min_amount_raw`（以 `GET /v1/topup/deposit-address` 回傳的實際值或 `GET /v1/topup/status` 回傳的 `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 秒**的間隔進行輪詢，不要過於頻繁，以避免觸發速率限制。

## 程式碼範例

以下範例示範如何在 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("Missing BLOCKVECTRA_API_KEY environment variable");
}

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

// 1. Check availability and read minimum deposit threshold
const statusRes = await fetch(`${BASE_URL}/v1/topup/status`);
const status = await statusRes.json();
if (!status.enabled) {
  throw new Error("Top-up is currently disabled");
}
const minDepositUsd = status.min_deposit_usd;
console.log("Minimum deposit (USD):", minDepositUsd);

// 2. Retrieve deposit address and open networks
const addressRes = await fetch(`${BASE_URL}/v1/topup/deposit-address`, {
  headers: { "x-api-key": apiKey },
});

if (addressRes.status === 401) {
  throw new Error("Missing or invalid API key (HTTP 401)");
}
if (addressRes.status === 409) {
  throw new Error("Top-up is disabled (topup_disabled)");
}
if (!addressRes.ok) {
  throw new Error(`Failed to retrieve deposit address: ${addressRes.status}`);
}

const depositData = await addressRes.json();
console.log("Deposit address:", depositData.address);
console.log("Open networks count:", depositData.networks.length);

// 3. Poll deposit status
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("Missing or invalid API key (HTTP 401)");
  }
  if (!res.ok) {
    throw new Error(`Failed to query deposits: ${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("Missing BLOCKVECTRA_API_KEY environment variable")

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

# 1. Check availability and read minimum deposit threshold
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("Top-up is currently disabled")
min_deposit_usd = status_data.get("min_deposit_usd")
print("Minimum deposit (USD):", min_deposit_usd)

# 2. Retrieve deposit address
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("Missing or invalid API key (HTTP 401)")
if resp.status_code == 409:
    raise RuntimeError("Top-up is disabled (topup_disabled)")
resp.raise_for_status()
deposit_data = resp.json()
print("Deposit address:", deposit_data["address"])

# 3. Poll deposit status
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("Missing or invalid API key (HTTP 401)")
    resp.raise_for_status()
    return resp.json()
```

## 下一步

* [查詢餘額（`GET /v1/account`）](https://docs.blockvectra.com/en/guides/billing-rules/#query-balance-get-v1account)，以驗證你的帳戶餘額與剩餘計算單位（CU）。
* [計費規則](https://docs.blockvectra.com/en/guides/billing-rules/)，以檢閱計算單位（CU）計量、速率限制與不計費錯誤。
* [免費方案指南](https://docs.blockvectra.com/en/guides/free-plan/)，以檢閱免費額度限制與升級規則。
* [程式化註冊指南](https://docs.blockvectra.com/en/guides/programmatic-signup/)，以使用錢包簽名建立帳戶並佈建 API key。
