# Gas Station：API 与钱包授权

> 原文地址: https://docs.blockvectra.com/zh/guides/gas-station/

## 访问与当前可用状态

Gas Station 使用独立的 HTTPS 服务地址和租户访问 token，由 Gas Station 运营方按既有接入安排提供。下列每个端点均须携带 `Authorization: Bearer <apiToken>`。BlockVectra RPC API key 不是该 token；本文不提供自助签发 token 的端点。妥善保管 token 和钱包私钥。租户仅从 token 得出，不从请求 body 或 URL 读取；token 和幂等 key 不放入 URL。

截至2026年10月21日，开发服务处于暂停状态，下列九条网络均未启用执行，服务费收取也未启用。链目录提供就绪信息，不承诺所列网络可以执行转账。规划执行前先读取 `GET /v1/chains`；执行尚不可用时，不应报价、签名或提交。

## 链目录

`GET /v1/chains` 返回 `{ "chains": [...] }`。每行包含 `slug`、`id`、`configured`、`runtimeVerified`、`readinessReasons` 和 `executionStrategy`。`configured` 仅表示配置情况，不证明执行成功或最终性已验证。当前九行均为 `configured=false`、`runtimeVerified=false`。

| 链 slug              | Chain ID |
| ------------------- | -------- |
| `arb_mainnet`       | 42161    |
| `base_mainnet`      | 8453     |
| `bsc_mainnet`       | 56       |
| `eth_mainnet`       | 1        |
| `eth_sepolia`       | 11155111 |
| `hyperevm_mainnet`  | 999      |
| `polygon_mainnet`   | 137      |
| `robinhood_mainnet` | 4663     |
| `robinhood_testnet` | 46630    |

## HTTP 端点

以下路径相对于接入时提供的 HTTPS Gas Station 服务地址，不是 BlockVectra RPC 路由。有 JSON body 的 POST 须设置 `Content-Type: application/json`；reconcile 请求不带 body。成功响应使用 HTTP 200。

| 方法与路径                          | 输入                                              | 响应                     |
| ------------------------------ | ----------------------------------------------- | ---------------------- |
| `GET /v1/chains`               | 无 body                                          | 链就绪目录                  |
| `POST /v1/quotes`              | 下述报价请求                                          | `Quote`                |
| `POST /v1/tasks`               | `quoteId`、`safeSignatures`、`envelopeSignatures` | `TaskView`             |
| `GET /v1/quotes/:quoteId/task` | 预先保存的 quote ID；无 body                           | 关联的 `TaskView`，或404    |
| `GET /v1/tasks/:id`            | 已知 task ID；无 body                               | `TaskView`，或404        |
| `POST /v1/tasks/:id/reconcile` | 已知 task ID；无 body                               | 观察后的 `TaskView`；绝不重发提交 |

报价请求恰含 `chain`、`safe`、`sponsor`、`token`、`recipient`、`amount`、`idempotencyKey`。使用目录中的链 slug，以及该访问权限允许的资助方、资产和收款人。`safe`、`token`、`recipient` 为非零 EVM 地址。`amount` 是 token 原生最小单位的正整数字符串，小于 `2^256`，不能使用浮点 token 金额。`chain`、`sponsor`、`idempotencyKey` 使用1–128个 `[A-Za-z0-9_.:-]` ASCII字符；quote ID 和 task ID 则使用1–128个 `[A-Za-z0-9_-]` 字符。

## 审查并授权报价

执行路径为 `SAFE_141_VIA_AA_V07`：服务 SimpleAccount 经 EntryPoint 0.7 调用客户 Safe 1.4.1，再执行 ERC-20 `transfer(recipient, amount)`。这是兼容 Safe 的流程，不是任意 EOA 的 gas 代付。EntryPoint 地址为 `0x0000000071727De22E5E9d8BAf0edAc6f37da032`，该地址本身不能证明部署或链已就绪。不提供 direct-relay 回退。

`Quote` 包含 `id`、`chain`、`chainId`、`safe`、`request`、`requestDigest`、`safeTypedData`、`envelopeTypedData`、`owners`、`threshold`、`expiresAt`。签名前审查资产、收款人、金额、策略、实际服务 relayer、费用上限、单位和有效期。`request.execution.sender`、`nonce` 分别是客户 Safe 与其 Safe nonce；服务账户和 UserOperation nonce 是独立身份，不得替换这些字段。请求金额和上限为非负十进制整数字符串；有效期时间戳为 Unix epoch 起算的毫秒数。

请求摘要为 canonical `{ domain: "gas-station-request-v2", ...request }` 的 SHA-256，使用不带 `0x` 的64位小写十六进制。Canonical JSON 对各层对象键排序、保持数组顺序、使用 UTF-8、不含空白。按返回的规范化 request 重算；envelope 使用相同摘要并加 `0x` 前缀，还绑定 SafeTx hash、实际 relayer 和有效期。SafeTx 本身没有到期或 relayer 字段；envelope 不改变 Safe 链上签名语义。

Owner 在本地分别签署 SafeTx 和 envelope。提交 `quoteId` 及两组签名数组，每项为 `{ owner, signature }`。两数组须包含相同的 `threshold` 个 owner，并按 owner 地址严格升序排列。签名格式为65字节 `r || s || v`，`v` 为27或28。绝不提交私钥。SDK 在签名前检查摘要与 typed-data 绑定，包括 `feeCaps.gas >= gasLimit * maxFeePerGas` 及 `0 <= maxPriorityFeePerGas <= maxFeePerGas`。SDK 校验不独立证明链部署或服务账户归属。

## 幂等与提交结果未知

同一租户内，幂等 key 绑定原报价意图。同 key 改变内容返回 `409 IDEMPOTENCY_CONFLICT`；重放 key 不会续期已过期报价。提交前保存 `quote.id`。SDK 每个方法只发送一次 HTTP 请求，不自动重试提交，不跟随重定向或切换服务地址。

提交超时、丢失响应，或成功响应不可读、不合格时，SDK 返回 `SUBMIT_OUTCOME_UNKNOWN`。使用原 quote ID 查询 `GET /v1/quotes/:quoteId/task`。该查询不预占、不签名、不赞助、不广播、不触发 reconcile。404仅表示此次查询没有该租户可见的关联任务，不能证明原提交从未受理。404或查询失败后仍保留未知结果，稍后显式再查，不生成新 key 或重新提交。获得 task ID 后保存，用于后续读取或 reconcile。

## 任务状态与交易身份

`TaskView` 包含 `id`、`chain`、`safe`、`state`、可选 `userOperationHash`、`transactionHash`、`businessSuccess`、`actualCosts`、可选 `costProjection`、`blockHash`、`errorCode`。

状态包括 `RESERVED`、`PREPARING`、`EXPIRED`、`SIGNING`、`SIGNED`、`SUBMITTING`、`UNKNOWN`、`ACCEPTED`、`INCLUDED`、`FINALIZED_SUCCEEDED`、`FINALIZED_FAILED`。`PREPARING` 是可查询的进度状态，不是服务错误。未知 hash 和金额保持 null；旧响应可省略 `userOperationHash`。GET 不签名或发送；reconcile 可以观察状态，但绝不签名或重广播。

`userOperationHash` 标识 AA 操作，`transactionHash` 标识外层链上交易，不能拿前者填充后者；`blockHash` 标识纳入区块。Hash 为 `0x` 加64位十六进制，仅格式合法不能证明上链或最终性。外层交易纳入或成功，不等于业务成功；应读取任务的可信业务结果与最终状态。费用尚未完整时仍保留最终业务结果；业务失败也可能消耗 gas。

## 已知与未知费用

`actualCosts` 只能是完整结算的 `{ gas, serviceFee, apiCu }` 或 `null`；null不等于免费。`FINALIZED_SUCCEEDED` 或 `FINALIZED_FAILED` 在费用未解决时仍可为 `actualCosts=null`。

`costProjection` 对 `gas`、`serviceFee`、`apiCu` 分别返回 `{ unit, knownSubtotal, complete }`。单位来自该任务 request；非null金额为该单位下的非负十进制整数字符串。`knownSubtotal=null` 表示没有可信金额；`complete=false` 的小计仍可能增加。`complete=true` 必须有非null金额；完整性本身不生成结算或证明付款。`serviceFee` 金额是可信记账事实，不代表已支付、capture或收款凭证。

结算后，投影金额与 `actualCosts` 相同，不再叠加 CU。结算前，gas和服务费使用可信最终观察。CU使用原报价的累计费用证据：全部请求未解决时仍为未知，部分解决时可以给出部分小计。只有全部尝试已解决、窗口已关闭且无观察冲突时，CU才完整。没有请求且无可信观察时，即使窗口关闭仍是未知；可信零值与缺证据不同。费用上限、HTTP状态、预算都不是实际费用。读取投影不触发额外费用证据请求或改变记账。

旧服务可省略 `costProjection`，SDK与CLI保持省略，不补零；附加投影畸形时仅忽略该投影，不改变原成功响应。本文不提供价格、收费端点或付费服务可用承诺。

## 错误与暂停行为

错误形如 `{ "error": { "code": "..." } }`。

| HTTP状态 | Code与含义                                                    |
| ------ | ---------------------------------------------------------- |
| 400    | `INVALID_REQUEST` 或 `INVALID_SIGNATURE`：输入或授权不合法           |
| 401    | `UNAUTHORIZED`：缺失或无效的 Gas Station token                    |
| 403    | `FORBIDDEN`：权限不允许该链、资助方、资产、收款人或金额                          |
| 404    | `NOT_FOUND`、`QUOTE_NOT_FOUND` 或 `TASK_NOT_FOUND`：没有可见资源    |
| 409    | `IDEMPOTENCY_CONFLICT`、`QUOTE_EXPIRED` 或 `BUDGET_EXCEEDED` |
| 429    | `RATE_LIMITED`：遵守返回的 `Retry-After`，不重放结果未知的提交              |
| 503    | `CHAIN_NOT_READY`：执行已暂停或所需链能力不可用                           |

暂停时，就绪检查以 `503 CHAIN_NOT_READY` 拒绝新执行。鉴权、输入校验、权限、报价过期和既有任务关联仍按各自规则处理，并非暂停时每个请求都返回503。已关联任务的相同重放只返回既有事实，不再次发送。状态读取与 reconcile 仍须通过鉴权和权限检查；暂停不会将未知结果变为失败，也不授权重发。
