地址转账监听 · 接口详情
交易查询模块
交易查询模块提供对链上交易数据的多维度查询能力,支持按地址查询历史列表、批量查询交易详情,以及跨链聚合查询。
接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /tx/api/v1/transfers/search | 按地址查询交易列表(游标分页) |
| POST | /tx/api/v1/transfers/multi-chain | 多链多地址聚合查询 |
| POST | /tx/api/v1/transactions/batch-get | 按交易哈希批量查询详情 |
| GET | /tx/api/v1/transactions/{tx_hash} | 查询单笔交易详情 |
地址交易查询
接口路径:POST /tx/api/v1/transfers/search
用途
按单个地址查询其在指定链上的历史交易列表,支持按交易类型、代币过滤,使用游标方式分页。
请求体字段说明
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
address | string | 是 | - | 待查询的钱包地址 |
chain_type | string | 否 | - | 链类型,不填则跨链查询 |
chain_id | integer | 否 | - | 链 ID |
tx_type | string | 否 | "" | 交易类型过滤,空字符串表示不过滤 |
token | string | 否 | - | 代币合约地址,用于过滤特定代币的转账 |
limit | integer | 否 | 20 | 每次返回的最大记录数,范围 1~100 |
id | integer | 否 | - | 游标 ID,用于分页续查 |
示例
# 基本查询
curl -X POST "$BASE_URL/tx/api/v1/transfers/search" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"chain_type": "ethereum",
"chain_id": 1,
"address": "0x1234567890abcdef1234567890abcdef12345678",
"limit": 20
}'
# 按代币过滤(查询 USDT 转账)
curl -X POST "$BASE_URL/tx/api/v1/transfers/search" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"chain_type": "ethereum",
"chain_id": 1,
"address": "0x1234...",
"token": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}'
响应
{
"code": 1,
"msg": "success",
"data": [
{
"chain_type": "ethereum",
"chain_id": 1,
"height": 21000000,
"tx_time": 1745000000,
"tx_hash": "0xabc123...",
"sender": "0x1234...",
"receiver": "0x5678...",
"amount": "1000000000000000000",
"symbol": "ETH",
"decimals": 18,
"tx_status": "success",
"transfer_type": "native",
"token_transfers": [],
"from_details": [],
"to_details": [],
"internal_transactions": []
}
]
}
注意:此接口成功时
code为1,消息字段为msg,与其他接口略有不同。
多地址聚合查询
接口路径:POST /tx/api/v1/transfers/multi-chain
用途
同时查询多条链、多个地址的交易记录,结果聚合后统一返回。
请求体字段说明
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
addresses | array | 是 | - | 多链地址分组列表,至少一项,最多 10 组 |
addresses[].chain_type | string | 是 | - | 链类型 |
addresses[].chain_id | integer | 是 | - | 链 ID |
addresses[].address | string[] | 是 | - | 该链下的地址数组,每组最多 20 个地址 |
limit | integer | 否 | 20 | 返回的最大记录数,范围 1~100 |
id | integer | 否 | - | 游标 ID,用于分页续查 |
示例
curl -X POST "$BASE_URL/tx/api/v1/transfers/multi-chain" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"addresses": [
{"chain_type": "ethereum", "chain_id": 1, "address": ["0xabc...001", "0xabc...002"]},
{"chain_type": "bsc", "chain_id": 56, "address": ["0xdef...003"]},
{"chain_type": "solana", "chain_id": 1, "address": ["SolanaAddress..."]}
],
"limit": 20
}'
响应
与 /tx/api/v1/transfers/search 格式相同,data 为聚合后的交易记录数组。
交易详情批量查询
接口路径:POST /tx/api/v1/transactions/batch-get
用途
根据交易哈希批量查询交易详情。
请求体字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
chain_type | string | 是 | 链类型 |
chain_id | integer | 是 | 链 ID |
tx_hash | string 或 string[] | 是 | 单个交易哈希或哈希数组 |
示例
# 查询单笔交易
curl -X POST "$BASE_URL/tx/api/v1/transactions/batch-get" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"chain_type": "ethereum", "chain_id": 1, "tx_hash": "0xabc123..."}'
# 批量查询多笔交易
curl -X POST "$BASE_URL/tx/api/v1/transactions/batch-get" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"chain_type": "ethereum", "chain_id": 1, "tx_hash": ["0xabc123...", "0xdef456..."]}'
单笔交易详情查询
接口路径:GET /tx/api/v1/transactions/{tx_hash}
用途
查询单笔交易的完整详情,返回结构与 /tx/api/v1/transfers/search、/tx/api/v1/transfers/multi-chain、/tx/api/v1/transactions/batch-get 中的 TransactionDetail 一致。与 /tx/api/v1/transactions/batch-get 的主要区别:
- 采用 GET 请求,参数通过 Query String 传递
- 支持
no_cache参数强制回源:当数据库中无记录时,可按链实时拉取并写入缓存 - 回源成功后会自动将数据写入数据库缓存
请求参数
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
chain_type | query | string | 是 | 链类型 |
chain_id | query | integer | 是 | 链 ID |
tx_hash | path | string | 是 | 交易哈希,支持带或不带 0x 前缀 |
no_cache | query | string | 否 | 传 true 或 1 时跳过本地缓存直接回源 |
示例
# 正常查询
curl "$BASE_URL/tx/api/v1/transactions/0xabc123...?chain_type=ethereum&chain_id=1"
# 强制回源查询
curl "$BASE_URL/tx/api/v1/transactions/0xabc123...?chain_type=ethereum&chain_id=1&no_cache=true"
响应(找到交易)
{
"code": 0,
"msg": "success",
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"height": 21000000,
"tx_time": 1745000000,
"tx_hash": "0xabc123...",
"tx_version": null,
"sender": "0x1234...",
"fee_payer": "0x1234...",
"receiver": "0x5678...",
"transfer_type": "native",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1000000000",
"gas_limit": "21000",
"gas_used": "21000",
"gas_price": "20000000000",
"tx_fee": "420000000000000",
"nonce": "42",
"symbol": "ETH",
"decimals": 18,
"amount": "1000000000000000000",
"tx_status": "success",
"method_id": "",
"method_call": "",
"l1_origin_hash": null,
"from_details": [],
"to_details": [],
"internal_transactions": [],
"token_transfers": []
}
}
响应(交易不存在)
{
"code": 0,
"msg": "success",
"data": null
}
交易数据结构详解
TransactionDetail(统一交易详情格式)
/tx/api/v1/transfers/search、/tx/api/v1/transfers/multi-chain、/tx/api/v1/transactions/batch-get、GET /tx/api/v1/transactions/{tx_hash} 接口返回的交易格式。
主体字段:
| 字段 | 类型 | 说明 |
|---|---|---|
chain_type | string | 归一化后的链类型,小写 |
chain_id | integer | 链 ID |
height | integer | null | 区块高度 |
tx_time | integer | null | 交易时间,秒级 Unix 时间戳 |
tx_hash | string | 交易哈希 |
tx_version | integer | null | 交易版本号(Solana 等使用) |
sender | string | null | 发送方地址 |
fee_payer | string | null | 手续费支付方地址(Solana 场景) |
receiver | string | null | 接收方地址 |
transfer_type | string | null | 交易类型,如 native(原生转账)、token(代币转账) |
amount | string | null | 交易金额,字符串格式避免精度丢失 |
symbol | string | null | 主币或代币符号,如 ETH、BNB |
decimals | integer | null | 精度位数,如 18 |
tx_status | string | null | 交易状态:success、failed、pending |
tx_fee | string | null | 交易手续费 |
gas_limit | string | null | Gas 上限(EVM) |
gas_used | string | null | 实际消耗 Gas(EVM) |
gas_price | string | null | Gas 单价(EVM) |
max_fee_per_gas | string | null | EIP-1559 最大 Gas 单价 |
max_priority_fee_per_gas | string | null | EIP-1559 优先费单价 |
nonce | string | null | 账户 Nonce(EVM) |
method_id | string | null | 合约调用方法选择器(EVM) |
method_call | string | null | 合约方法名或调用摘要 |
l1_origin_hash | string | null | 二层链对应的 L1 原始交易哈希 |
token_transfers | array | 代币转账明细列表 |
from_details | array | 输入地址明细(UTXO 模型) |
to_details | array | 输出地址明细(UTXO 模型) |
internal_transactions | array | 内部交易或 Solana 指令明细 |
token_transfers(代币转账明细):
| 字段 | 类型 | 说明 |
|---|---|---|
from_address | string | null | 代币转出地址 |
to_address | string | null | 代币转入地址 |
token_contract_address | string | null | 代币合约地址 |
symbol | string | null | 代币符号 |
amount | string | null | 转账数量 |
decimals | integer | null | 代币精度 |
is_from_contract | boolean | 转出方是否为合约地址 |
is_to_contract | boolean | 转入方是否为合约地址 |
from_token_address | string | null | 源 token 账户地址(Solana) |
to_token_address | string | null | 目标 token 账户地址(Solana) |
mint | string | null | Solana Mint 地址 |
program_id | string | null | Solana Program ID |
from_details(输入地址明细,UTXO 模型):
| 字段 | 类型 | 说明 |
|---|---|---|
address | string | null | 输入地址 |
amount | string | null | 输入金额 |
is_contract | boolean | 是否为合约地址 |
vin_index | string | null | UTXO 输入索引 |
pre_vout_index | string | null | 前序交易输出索引 |
ref_tx_hash | string | null | 前序引用交易哈希 |
to_details(输出地址明细,UTXO 模型):
| 字段 | 类型 | 说明 |
|---|---|---|
address | string | null | 输出地址 |
amount | string | null | 输出金额 |
is_contract | boolean | 是否为合约地址 |
vout_index | string | null | UTXO 输出索引 |
internal_transactions(内部交易 / Solana 指令):
| 字段 | 类型 | 说明 |
|---|---|---|
from_address | string | null | 内部调用发送方 |
to_address | string | null | 内部调用接收方 |
amount | string | null | 内部调用金额 |
tx_status | string | null | 内部调用状态 |
program_id | string | null | Solana Program ID |
instr_index | integer | null | Solana 指令索引 |
depth | integer | null | Solana 调用深度 |
instruction | string | null | Solana 指令名称 |
description | string | null | 指令说明 |
分页说明
交易列表查询使用游标(Cursor)分页而非传统的页码分页:
- 首次请求时不传
id参数,获取最新的 N 条记录 - 如果返回记录数量等于
limit,说明可能还有更多数据 - 取返回数组中最后一条记录的数据库内部
id传给下一次请求的id参数 - 继续请求,直到返回记录数量少于
limit为止
注意:游标分页不支持跳页,只能顺序向前翻页。
金额处理建议
所有金额字段均以字符串形式返回,以避免大数精度问题。展示时需结合 decimals 字段进行换算:
function formatAmount(amount, decimals) {
if (!amount || decimals === null) return '0';
const bigAmount = BigInt(amount);
const divisor = BigInt(10 ** decimals);
const intPart = bigAmount / divisor;
const fracPart = bigAmount % divisor;
return `${intPart}.${fracPart.toString().padStart(decimals, '0')}`;
}
// 示例:1000000000000000000 (decimals=18) → "1.000000000000000000"
console.log(formatAmount('1000000000000000000', 18));
DEX Pool 最近交易接口
DEX Pool 模块用于维护需要监听的 Pool 地址,并提供最近交易查询。相关订阅和推送仍使用地址管理接口或 WebSocket subscribe。
当链上交易处理成功后,系统会用交易参与地址匹配已启用的 Pool 地址。命中后将交易写入该 Pool 对应的 Durable Object FIFO 队列,队列容量默认最多保留最近 100 条。查询接口对外返回统一的 TransactionDetail 结构。
查询 Pool 最近交易
接口路径:GET /tx/api/v1/dex-pools/{poolAddress}/trades
用途
公开查询指定 Pool 的最近交易记录,不需要额外管理员鉴权。
Query 参数
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
chainType | string | 是 | 无 | 链类型。 |
chainId | number | 是 | 无 | 链 ID。 |
limit | number | 否 | 100 | 返回最近交易条数,范围 1 到 100。 |
示例
curl "$BASE_URL/tx/api/v1/dex-pools/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8/trades?chainType=evm&chainId=1&limit=50"
成功响应
{
"code": 0,
"message": "ok",
"data": {
"items": [
{
"chain_type": "evm",
"chain_id": 1,
"height": 19600000,
"tx_time": 1710000000,
"tx_hash": "0x...",
"sender": "0xsender...",
"fee_payer": "0xsender...",
"receiver": "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
"transfer_type": "Token",
"gas_limit": "21000",
"gas_used": "21000",
"gas_price": "1000000000",
"max_fee_per_gas": "",
"max_priority_fee_per_gas": "",
"tx_fee": "21000000000000",
"nonce": "1",
"symbol": "ETH",
"decimals": 18,
"amount": "0",
"tx_status": "success",
"method_id": "0xa9059cbb",
"method_call": "0x...",
"from_details": [],
"to_details": [],
"internal_transactions": [],
"token_transfers": [
{
"from_address": "0xsender...",
"to_address": "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
"is_from_contract": false,
"is_to_contract": false,
"token_contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC",
"amount": "1000000",
"decimals": 6
}
]
}
]
}
}
返回交易字段
data.items 为 TransactionDetail 数组,字段说明和 token_transfers、from_details、to_details、internal_transactions 子结构请参考该章节。
地址管理模块
地址管理模块用于维护每个业务应用的监听地址名单。只有添加到名单中的地址,其链上交易才会被推送到对应应用的 WebSocket 订阅者或 Webhook 回调。
接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| PATCH | /tx/api/v1/addresses | Patch 模式:同时增删地址 |
| POST | /tx/api/v1/addresses | 批量添加地址 |
| POST | /tx/api/v1/addresses/batch-delete | 批量移除地址 |
| POST | /tx/api/v1/addresses/contains | 查询地址是否存在于名单 |
公共鉴权说明
所有地址受保护接口都通过 X-API-Key 识别调用方。服务会基于该 Key 完成接口鉴权、业务归属识别和计费统计。
| 方式 | 说明 | 示例 |
|---|---|---|
| 请求头 | 公共 API Key | X-API-Key: your-api-key |
注意:公网接入只需传入
X-API-Key,不需要额外的应用标识请求头或查询参数。
添加监听地址接口
接口路径:POST /tx/api/v1/addresses
用途
向指定应用的监听地址名单中批量添加地址。支持同时为多条链添加地址。
链特殊处理行为
- 其他非 EVM 链:会尝试将地址同步到平台内部监听组件;同步失败不会阻断本地入库。
请求体字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
wallets | array | 是 | 按链分组的地址列表,至少一项 |
wallets[].chain_type | string | 是 | 链类型,如 ethereum、solana、tron |
wallets[].address | string[] | 是 | 该链下的地址数组,至少一个地址 |
示例
# 添加以太坊地址
curl -X POST "$BASE_URL/tx/api/v1/addresses" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"wallets": [
{
"chain_type": "ethereum",
"address": ["0x1234...", "0xabcd..."]
}
]
}'
# 同时添加多链地址
curl -X POST "$BASE_URL/tx/api/v1/addresses" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"wallets": [
{"chain_type": "ethereum", "address": ["0x1234..."]},
{"chain_type": "tron", "address": ["TJDENsfBJs4RFETt1X1uyT9pBxdnAbFnbm"]},
{"chain_type": "solana", "address": ["SolanaAddress..."]}
]
}'
响应
{
"code": 0,
"message": "ok",
"data": "success"
}
移除监听地址
接口路径:POST /tx/api/v1/addresses/batch-delete
用途
从指定应用的监听地址名单中批量移除地址。移除后,该地址的新交易将不再推送给该应用。
请求体字段说明
与 /tx/api/v1/addresses 相同:wallets[].chain_type + wallets[].address。
示例
curl -X POST "$BASE_URL/tx/api/v1/addresses/batch-delete" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"wallets": [
{"chain_type": "ethereum", "address": ["0x1234..."]}
]
}'
响应
{
"code": 0,
"message": "ok",
"data": "success"
}
地址名单 Patch 更新
接口路径:PATCH /tx/api/v1/addresses
用途
以 Patch 模式同时对某条链执行地址的增删操作,在一次请求中完成地址名单的部分更新。
请求体字段说明
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
chain_type | string | 是 | - | 链类型,此接口一次只操作一条链 |
adds | string[] | 否 | [] | 要加入名单的地址列表 |
removes | string[] | 否 | [] | 要移出名单的地址列表 |
示例
curl -X PATCH "$BASE_URL/tx/api/v1/addresses" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"chain_type": "ethereum",
"adds": ["0xNewAddress001...", "0xNewAddress002..."],
"removes": ["0xOldAddress001..."]
}'
响应
{
"code": 0,
"message": "ok",
"data": "success"
}
地址存在性查询
接口路径:POST /tx/api/v1/addresses/contains
用途
查询某个地址是否已在指定链的地址名单中。
说明:当前版本此接口只验证地址在全局地址表中是否存在,不按 API Key 隔离过滤。
请求体字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
chain_type | string | 是 | 链类型 |
address | string | 是 | 待查询的地址 |
示例
curl -X POST "$BASE_URL/tx/api/v1/addresses/contains" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"chain_type": "ethereum", "address": "0x1234..."}'
响应
{
"code": 0,
"message": "ok",
"data": {
"is_exist": true
}
}
地址管理最佳实践
地址格式规范:
- EVM 链:使用
0x开头的 Hex 格式地址,建议使用 EIP-55 checksum 格式 - Solana:使用 Base58 格式地址
- Tron:使用 Base58Check 格式(以
T开头) - Bitcoin:使用标准 Bitcoin 地址格式(P2PKH、P2SH、Bech32 等)
地址名单与 WebSocket 订阅的关系:
- 通过
/tx/api/v1/addresses添加的地址会被持久化到数据库,应用重启后仍然有效 - WebSocket 连接建立后,服务会自动监听该应用名单中的所有地址
- 也可以在 WebSocket 连接建立后,通过
subscribe命令动态追加监听,但这种方式仅对当前连接有效,断线后不会自动恢复
推荐使用 HTTP 接口管理地址名单,WebSocket 的 subscribe/unsubscribe 命令仅用于临时的细粒度控制。
Webhook 回调推送
Webhook 模块用于把监听地址命中的链上交易主动推送到接入方提供的 HTTP 回调地址。它适合服务端到服务端集成:接入方无需保持 WebSocket 长连接,只需提供一个可公网访问的 HTTPS 接口并配置到当前应用。
定义 Webhook 回调接口
当监听地址命中新交易,且当前应用满足 enable_webhook=true、webhook_url 已配置、is_active=true 时,服务端会向 webhook_url 发送请求。
POST https://example.com/tx-history/webhook
Content-Type: application/json
Sign: replace-with-shared-secret
| 项 | 说明 |
|---|---|
| 请求方法 | 固定为 POST |
Content-Type | 固定为 application/json |
Sign | 配置的 webhook_secret;未配置时为空字符串 |
| 请求体 | TransactionDetail[],即交易对象数组 |
| 响应要求 | 建议在业务接收成功后返回任意 2xx 状态码;响应体不做格式要求 |
说明:当前版本的
Sign是共享密钥透传,不是 HMAC 摘要签名。接入方应使用常量时间比较校验该值,并只接受 HTTPS 回调。
回调 Body 示例
Webhook 请求体没有外层 code、message 或 data 包装,直接是交易数组。同一次回调可能包含一笔或多笔交易。数组元素结构为 TransactionDetail。
[
{
"chain_type": "evm",
"chain_id": 1,
"height": 21000000,
"tx_time": 1745000000,
"tx_hash": "0xabc123...",
"sender": "0x1234...",
"fee_payer": "0x1234...",
"receiver": "0x5678...",
"transfer_type": "NativeCoin",
"gas_limit": "21000",
"gas_used": "21000",
"gas_price": "20000000000",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1000000000",
"tx_fee": "420000000000000",
"nonce": "42",
"symbol": "ETH",
"decimals": 18,
"amount": "1000000000000000000",
"tx_status": "success",
"method_id": "",
"method_call": "",
"from_details": [],
"to_details": [],
"internal_transactions": [],
"token_transfers": []
}
]
ERC-20 / Token 转账示例:
[
{
"chain_type": "evm",
"chain_id": 1,
"height": 21000001,
"tx_time": 1745000005,
"tx_hash": "0xdef456...",
"sender": "0x1234...",
"fee_payer": "0x1234...",
"receiver": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"transfer_type": "Token",
"gas_limit": "65000",
"gas_used": "52000",
"gas_price": "20000000000",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1000000000",
"tx_fee": "1040000000000000",
"nonce": "43",
"symbol": "ETH",
"decimals": 18,
"amount": "0",
"tx_status": "success",
"method_id": "0xa9059cbb",
"method_call": "transfer(address,uint256)",
"from_details": [],
"to_details": [],
"internal_transactions": [],
"token_transfers": [
{
"from_address": "0x1234...",
"to_address": "0x5678...",
"is_from_contract": false,
"is_to_contract": false,
"token_contract_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"amount": "1000000000",
"decimals": 6
}
]
}
]
Webhook 交易字段
Webhook body 中的数组元素以 TransactionDetail 为标准结构,字段说明和 token_transfers、from_details、to_details、internal_transactions 子结构请参考该章节。
接入方处理建议
- 鉴权:校验请求头
Sign是否等于配置的webhook_secret,未通过时返回401或403。 - 幂等:以
chain_type + chain_id + tx_hash作为主幂等键;如果一笔交易内有多条代币明细,可结合token_contract_address + from_address + to_address + amount做业务明细去重。 - 快速响应:建议先持久化原始 body 或写入队列,再异步执行业务处理,避免回调接口耗时过长。
- 金额处理:所有金额字段均为字符串,应结合对应
decimals换算展示金额,避免使用 JavaScriptnumber直接承载大整数。 - 异常重试:服务端提供 Webhook 持久化重试,单批最多 100 条交易,最长重试窗口 36 小时;接入方仍应以
chain_type + chain_id + tx_hash做幂等处理,并保留历史查询补偿能力。
接入配置
Webhook 开关和回调地址通过 /tx/api/v1/app/config 配置,接口通过 X-API-Key 识别当前应用:
curl "$BASE_URL/tx/api/v1/app/config" \
-H "X-API-Key: your-api-key"
配置字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
enable_ws | boolean | 否 | 是否启用 WebSocket 推送 |
enable_webhook | boolean | 否 | 是否启用 Webhook 推送 |
webhook_url | string | null | 否 | 接入方回调地址,必须是合法 URL;传 null 表示清空 |
webhook_secret | string | null | 否 | 共享密钥。服务端推送时会原样放入请求头 Sign |
ws_ack_resend_interval_seconds | integer | 否 | WebSocket ACK 重发间隔,单位秒 |
is_active | boolean | 否 | 是否启用当前应用配置,仅 PUT 支持 |
注意:
GET /tx/api/v1/app/config不会返回webhook_secret。如果需要轮换密钥,请通过POST或PUT重新设置。
开启 Webhook
curl -X PUT "$BASE_URL/tx/api/v1/app/config" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"enable_webhook": true,
"webhook_url": "https://example.com/tx-history/webhook",
"webhook_secret": "replace-with-shared-secret"
}'
成功响应:
{
"code": 0,
"message": "ok",
"data": "success"
}
查询配置响应示例:
{
"code": 0,
"message": "ok",
"data": {
"app_id": "current-business-identity",
"enable_ws": true,
"enable_webhook": true,
"webhook_url": "https://example.com/tx-history/webhook",
"ws_ack_resend_interval_seconds": 60,
"webhook_delivery_status": "active",
"webhook_retrying_since": null,
"webhook_last_failed_transaction_id": null,
"webhook_last_failure_reason": null,
"is_active": true,
"created_at": "2026-05-21T06:00:00.000Z",
"updated_at": "2026-05-21T06:10:00.000Z"
}
}
关闭或清空 Webhook
curl -X PUT "$BASE_URL/tx/api/v1/app/config" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"enable_webhook": false,
"webhook_url": null
}'
Webhook 持久化重试
当 Webhook 首次推送失败时,服务会将本批交易写入持久化重试队列,并把当前应用标记为 webhook_delivery_status=retrying。处于重试状态时,后续命中的交易不会继续直接请求订阅方服务,而是全部进入队列。
重试由独立调度器执行,间隔从 1s 开始渐进增长,最长到 36h。每次重试会从数据库读取待推送交易详情,并按最多 100 条交易组成一个 TransactionDetail[] 批量 POST 到 webhook_url。只要订阅方返回任意 2xx 状态码,本批即视为投递成功。
如果某批消息从首次失败起超过 36h 仍无法成功投递,服务会自动将 enable_webhook 更新为 false,并在应用配置中记录失败交易 ID 与最后失败原因。订阅方修复服务后,需要重新开启 Webhook。
Webhook 投递状态字段
| 字段 | 类型 | 说明 |
|---|---|---|
webhook_delivery_status | string | Webhook 投递状态:active 正常推送,retrying 正在持久化重试,disabled 已因连续失败自动关闭 |
webhook_retrying_since | integer | null | 进入重试状态的秒级 Unix 时间戳 |
webhook_last_failed_transaction_id | integer | null | 超过 36 小时仍失败时记录的内部交易 ID |
webhook_last_failure_reason | string | null | 最近一次或最终失败原因 |
查询重试状态
接口路径:GET /tx/api/v1/app/webhook/retry/status
curl "$BASE_URL/tx/api/v1/app/webhook/retry/status" \
-H "X-API-Key: your-api-key"
响应示例:
{
"code": 0,
"message": "ok",
"data": {
"app_id": "current-business-identity",
"enable_webhook": true,
"webhook_delivery_status": "retrying",
"webhook_retrying_since": 1779680000,
"webhook_last_failed_transaction_id": null,
"webhook_last_failure_reason": "Webhook HTTP 503: service unavailable",
"pending": 42,
"next_retry_at": 1779680060
}
}
手动触发重试
接口路径:POST /tx/api/v1/app/webhook/retry
订阅方服务恢复后,可手动触发一次立即重试,加快积压消息投递:
curl -X POST "$BASE_URL/tx/api/v1/app/webhook/retry" \
-H "X-API-Key: your-api-key"
响应示例:
{
"code": 0,
"message": "ok",
"data": {
"code": 0,
"message": "ok",
"data": {
"attempted": 42,
"delivered": 42,
"failed": 0,
"finalFailed": false,
"nextRetryAt": null,
"pending": 0
}
}
}
WebSocket 实时推送
WebSocket 模块提供链上交易的实时推送能力。客户端建立 WebSocket 连接后,服务会在监听地址产生链上交易时立即推送消息。
连接端点:
GET /tx/api/v1/transaction/ws
建立连接
请求说明
WebSocket 握手需要以下请求头:
| 请求头 | 类型 | 必填 | 说明 |
|---|---|---|---|
Upgrade | string | 是 | 固定值 websocket |
X-API-Key | string | 是 | 公共 API Key,用于鉴权和计费识别 |
X-ResendDuration | string | 否 | 历史消息重发时间窗口(秒),默认 180 |
注意:缺少
X-API-Key时,连接请求会返回400或鉴权失败。
连接示例
# 使用 wscat
wscat -c "wss://api.gelabs.org/tx/api/v1/transaction/ws" \
-H "X-API-Key: your-api-key"
连接成功消息
连接建立后,服务端立即推送:
{
"id": "msg-uuid-001",
"time": 1745000000000,
"type": "connected",
"code": 0,
"data": {
"clientId": "client-abc123",
"appId": "current-business-identity"
},
"msg": "success"
}
| 字段 | 说明 |
|---|---|
data.clientId | 当前连接在服务端的唯一标识,断线重连后会变化 |
data.appId | 当前连接绑定的调用方身份 |
连接失败
| HTTP 状态码 | 原因 | 处理建议 |
|---|---|---|
400 | 未提供 API Key | 确认已正确传入 X-API-Key |
426 | Upgrade: websocket 头缺失 | 确认使用 WebSocket 协议连接 |
消息格式
所有 WebSocket 消息均使用 JSON 格式,遵循以下统一结构:
{
"id": "消息唯一 ID",
"time": 1745000000000,
"type": "消息类型",
"code": 0,
"data": {},
"msg": "success"
}
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 消息唯一 ID,由发送方生成 |
time | integer | 消息时间戳,毫秒级 Unix 时间戳 |
type | string | 消息类型,决定消息的业务含义 |
code | integer | 业务状态码,0 表示成功,非 0 表示错误 |
data | object | null | 消息业务数据 |
msg | string | 文本消息,成功时通常为 success |
客户端命令
客户端可以向服务端发送以下命令。每条命令都需要提供唯一的 id 字段,服务端的回执会使用相同的 id 值。
ping(心跳)
发送心跳以保持连接活跃。
发送:
{
"id": "ping-001",
"type": "ping",
"data": {}
}
服务端回执:
{
"id": "ping-001",
"time": 1745000000000,
"type": "pong",
"code": 0,
"data": null,
"msg": "success"
}
subscribe(订阅地址)
订阅指定地址在指定链上的实时交易推送。
说明:通过此命令订阅的地址仅对当前连接有效,连接断开后不会保存。如需持久化地址订阅,请使用地址受保护接口提前将地址加入名单。
发送:
{
"id": "cmd-001",
"type": "subscribe",
"data": {
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain_type": "ethereum"
}
}
命令参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data.address | string | 是 | 待订阅的地址 |
data.chain_type | string | 是 | 链类型,服务端会转为小写 |
成功回执:
{
"id": "cmd-001",
"type": "subscribe",
"code": 0,
"data": {"API Key": "current-business-identity", "address": "0x1234...", "chain_type": "ethereum"},
"msg": "success"
}
错误回执:
{
"id": "cmd-001",
"type": "subscribe",
"code": 400,
"data": null,
"msg": "address and chain_type required"
}
unsubscribe(取消订阅)
取消对指定地址的实时交易订阅。
发送(取消特定链):
{
"id": "cmd-002",
"type": "unsubscribe",
"data": {
"address": "0x1234...",
"chain_type": "ethereum"
}
}
发送(取消所有链,省略 chain_type):
{
"id": "cmd-002",
"type": "unsubscribe",
"data": {
"address": "0x1234..."
}
}
命令参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data.address | string | 是 | 待取消订阅的地址 |
data.chain_type | string | 否 | 链类型,不填则取消该地址所有链的订阅 |
成功回执:
{
"id": "cmd-002",
"type": "unsubscribe",
"code": 0,
"data": {"address": "0x1234...", "chainType": "ethereum"},
"msg": "success"
}
取消所有链时,chainType 为 null。
getTxHash(查询交易详情)
通过 WebSocket 连接查询单笔交易的完整详情。
发送:
{
"id": "cmd-003",
"type": "getTxHash",
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"txHash": "0xabc123..."
}
}
命令参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data.chain_type | string | 是 | 链类型 |
data.chain_id | integer | 是 | 链 ID |
data.txHash | string | 否 | 交易哈希 |
data.messageId | string | 否 | 实时推送消息的唯一 ID,优先使用此字段 |
建议:优先使用
messageId(即推送消息的id字段值)查询,服务端可更精确地定位记录。
错误回执(交易不存在):
{
"id": "cmd-003",
"type": "getTxHash",
"code": 404,
"data": null,
"msg": "transaction not found"
}
transactionACK(确认已处理交易)
客户端收到 transaction 消息并成功处理后,应发送 ACK 确认。服务端收到 ACK 后会停止对该笔交易的重发逻辑。
重要:如果客户端长时间未发送 ACK,服务端可能在客户端重连后重新推送未确认的交易,以确保消息不丢失。
发送:
{
"id": "ack-001",
"type": "transactionACK",
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"txHash": "0xabc123..."
}
}
成功回执:
{
"id": "ack-001",
"type": "transactionACK",
"code": 0,
"data": {"txHash": "0xabc123..."},
"msg": "success"
}
服务端推送消息
transaction(实时交易推送)
当监听地址产生链上交易时,服务端主动推送:
{
"id": "msg-tx-uuid-001",
"time": 1745000000000,
"type": "transaction",
"code": 0,
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"height": 21000000,
"tx_time": 1745000000,
"tx_hash": "0xabc123...",
"sender": "0x1234...",
"fee_payer": "0x1234...",
"receiver": "0x5678...",
"transfer_type": "native",
"gas_limit": "21000",
"gas_used": "21000",
"gas_price": "20000000000",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1000000000",
"tx_fee": "420000000000000",
"nonce": "42",
"symbol": "ETH",
"decimals": 18,
"amount": "1000000000000000000",
"tx_status": "success",
"method_id": "",
"method_call": "",
"from_details": [],
"to_details": [],
"internal_transactions": [],
"token_transfers": [],
"listenAddress": ["0x1234..."],
"is_reorg_resend": false
},
"msg": "success"
}
交易消息关键字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
data.listenAddress | string[] | 命中监听规则的地址列表,一笔交易可能命中多个地址 |
data.is_reorg_resend | boolean | 是否为链重组后的重发消息,true 时需检查本地记录 |
data.token_transfers | array | 代币转账明细列表,ERC-20 转账时包含具体转账信息 |
data.tx_time | integer | 交易时间戳,秒级 Unix 时间戳 |
is_reorg_resend 说明:
当值为 true 时,表示此交易因**链重组(Reorg)**被重新推送。建议检查本地是否已存在该 tx_hash 的记录,并根据需要更新区块高度等信息。
ERC-20 代币转账消息示例:
{
"id": "msg-tx-002",
"type": "transaction",
"code": 0,
"data": {
"chain_type": "ethereum",
"tx_hash": "0xdef456...",
"transfer_type": "token",
"token_transfers": [
{
"from_address": "0x1234...",
"to_address": "0x5678...",
"is_from_contract": false,
"is_to_contract": false,
"token_contract_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"amount": "1000000000",
"decimals": 6,
"from_token_address": "",
"to_token_address": "",
"mint": "",
"program_id": ""
}
],
"listenAddress": ["0x1234..."],
"is_reorg_resend": false
},
"msg": "success"
}
HTTP 辅助接口
WebSocket 连接状态查询
接口路径:GET /tx/api/v1/transaction/ws/status
查询指定应用的 WebSocket 连接状态。
curl "$BASE_URL/tx/api/v1/transaction/ws/status" \
-H "X-API-Key: your-api-key"
调用方身份由 X-API-Key 识别;公网接入不需要额外传入应用标识查询参数。
响应:
{
"code": 0,
"message": "ok",
"data": {
"appId": "current-business-identity",
"connectedClients": 2,
"clients": ["client-abc123", "client-def456"],
"details": [
{
"clientId": "client-abc123",
"connectedAt": 1745000000000,
"uptime": 3600,
"pendingTxCount": 0
}
]
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
connectedClients | integer | 当前在线客户端数量 |
details[].uptime | integer | 已存活秒数 |
details[].pendingTxCount | integer | 尚未收到 ACK 的待确认交易数量 |
WebSocket 消息重放
接口路径:POST /tx/api/v1/transaction/ws/replay
手动重放一条漏发的交易消息。服务端会:
- 将消息幂等写入数据库
- 推送给当前在线的 WebSocket 订阅者
- 并行触发 Webhook 回调
请求体为一条完整的 WsTransactionMessage 格式消息(与服务端推送的 transaction 消息格式相同)。
响应:
{
"code": 0,
"message": "ok",
"data": {
"processed": true,
"txhash": "0xabc123..."
}
}
错误处理
WebSocket 命令的错误以相同的 JSON 格式通过 WebSocket 消息返回,code 字段为非 0:
{
"id": "原始命令的 id",
"type": "命令类型",
"code": 400,
"data": null,
"msg": "错误描述"
}
常见错误汇总:
| 命令 | code | msg |
|---|---|---|
subscribe | 400 | address and chain_type required |
unsubscribe | 400 | address required |
getTxHash | 400 | chain_type, chain_id, txHash required |
getTxHash | 404 | transaction not found |
transactionACK | 400 | chain_type and messageId/txHash required |
最佳实践
心跳保活
建议每 30 秒发送一次 ping 命令:
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ id: `ping-${Date.now()}`, type: 'ping', data: {} }));
}
}, 30000);
断线重连
function connect() {
const ws = new WebSocket(WS_URL, { headers: { 'X-API-Key': API_KEY } });
ws.on('close', () => {
console.log('连接断开,5 秒后重连...');
setTimeout(connect, 5000);
});
return ws;
}
ACK 策略
- 及时 ACK:收到
transaction消息并完成业务处理后,立即发送transactionACK - 避免漏 ACK:如果业务处理耗时较长,可先 ACK 再异步处理
- ACK 去重:业务层应以
tx_hash + chain_type + chain_id为唯一键做幂等处理
地址订阅策略
| 方式 | 持久性 | 适用场景 |
|---|---|---|
HTTP 地址管理(/tx/api/v1/addresses) | 持久,重启后有效 | 常规业务地址的长期监听 |
WebSocket subscribe 命令 | 临时,仅当前连接有效 | 临时调试或动态细粒度控制 |
并发连接
同一个 API Key 对应的业务身份支持多个客户端同时连接,服务端会向所有在线连接广播交易消息。适用于多实例水平扩展或主备切换场景(需业务层做幂等处理)。