HyperEVM RPC 限流與日誌回填
處理 HyperEVM RPC 限流與 429 回應,有界呼叫認證 eth_getLogs,儲存游標並復原缺失活動。
直接答案
預設官方 HyperEVM 公共 RPC 的 eth_getLogs 單次查詢最多 50 個區塊(出處見官方 JSON-RPC 文件)。在 BlockVectra 上,認證的 eth_getLogs 請求單次最多涵蓋 1,000 個區塊(來自 GET /v1/chains 中的 hyperevm_mainnet.max_logs_block_range,包含兩端)。跨度超限回傳 HTTP 200、JSON-RPC -32602 與 logs_range_too_large,retryable: false(見錯誤目錄);按 [from, min(from + max − 1, end)] 分段並儲存 cursor(游標)以便斷點續跑,成功後從末尾加一繼續。官方公共 RPC 的每 IP 速率限制與 BlockVectra 的 key 限額分別見官方公共 RPC 限流與 429 及下方服務參數。
這篇指南協助你完成的任務
- 探測 HyperEVM RPC:在選擇認證方法前,先使用 viem 或 ethers 進行公共讀取。
- 回填有界日誌範圍:在 HyperEVM
eth_getLogs限制內回填,並根據回傳的錯誤做出重試判斷。 - 讀取地址活動:使用 key 查詢已索引的交易與轉帳,檢查回傳的覆蓋範圍與新鮮度中繼資料。
三步驟任務:回填限定區塊範圍的 HyperEVM 日誌
先免 key 讀取最新區塊,建立 key,然後查詢合約在有限區塊範圍內的事件日誌。
選擇你所需的合約與區塊範圍。本任務只涵蓋該有界範圍;不保證完整的合約歷史。
1. 免 API key 讀取最新區塊
curl -sS "https://api.blockvectra.com/v1/hyperevm_mainnet/public" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'JSON-RPC 的 result 是十六進位的最新區塊編號。這是由 GET /v1/chains 發布的 HyperEVM public.url。公開端點的 public.methods 不包含 eth_getLogs;步驟 3 需要 key。
2. 建立 API key
為此回填建立 key。建立 key 並儲存對話方塊中顯示的 secret,以用於 hyperevm_mainnet。
對於透過 HTTP 且不使用瀏覽器的 AI Agent,請參考程式化註冊指南。在 POST /auth/siwe/login JSON 請求主體中傳入指南 URL 的有效 ref,而不是範例中的 docs-signup;若無來源則省略。切勿要求使用者將 key 貼入對話中。
3. 使用你的 key 回填日誌
完整入門範本:blockvectra/hyperevm-backfill
將以下指令碼儲存為 hyperevm-task.ts。它可在 Node.js 24 或更高版本下執行,無需額外套件。將 BLOCKVECTRA_API_KEY 設定為你儲存的 key,並將 LOG_ADDRESS 設定為你要檢查的發出合約地址;將 key 保留在你的伺服器或本機終端機中。
export BLOCKVECTRA_API_KEY='replace-with-your-key'
export LOG_ADDRESS='replace-with-contract-address'
node hyperevm-task.ts指令碼預設查詢最近的 max_logs_block_range 個區塊,接近創世區塊時查詢實際可用的區塊數。此上限在執行時從 /v1/chains 讀取。若要選擇其他有限範圍,請在執行前同時設定 FROM_BLOCK 與 TO_BLOCK,值可用十進位或 0x 十六進位區塊號。超過單次上限的範圍會拆分為連續區段,每段跨度不超過公開上限。
const apiKey = process.env.BLOCKVECTRA_API_KEY;
const address = process.env.LOG_ADDRESS;
if (!apiKey || apiKey === "replace-with-your-key") throw new Error("Set BLOCKVECTRA_API_KEY");
if (!address || !/^0x[0-9a-f]{40}$/i.test(address)) throw new Error("Set LOG_ADDRESS to a contract address");
const fromBlock = process.env.FROM_BLOCK;
const toBlock = process.env.TO_BLOCK;
if ((fromBlock !== undefined || toBlock !== undefined) && (!fromBlock || !toBlock)) {
throw new Error("Set both FROM_BLOCK and TO_BLOCK");
}
const chainsUrl = "https://api.blockvectra.com/v1/chains";
const rpcUrl = new URL("./hyperevm_mainnet", chainsUrl).href;
async function readJson(url: string) {
const response = await fetch(url, { signal: AbortSignal.timeout(15_000) });
if (!response.ok) throw new Error(`Metadata HTTP ${response.status}`);
return response.json();
}
const catalog = await readJson(chainsUrl);
const chain = catalog.chains.find((item: { chain: string }) => item.chain === "hyperevm_mainnet");
if (!chain || !Number.isSafeInteger(chain.max_logs_block_range) || chain.max_logs_block_range <= 0) {
throw new Error("Missing or invalid max_logs_block_range");
}
if (!chain.methods.allow.includes("eth_getLogs") || chain.methods.deny.includes("eth_getLogs")) {
throw new Error("eth_getLogs is unavailable on this chain");
}
const maxRange = BigInt(chain.max_logs_block_range);
const plans = await readJson("https://console-api.blockvectra.com/v1/plans");
function weight(method: string): bigint {
const row = plans.method_weights.find((item: { method: string }) => item.method === method);
if (!row || !Number.isSafeInteger(row.cu_weight) || row.cu_weight <= 0) {
throw new Error(`Missing or invalid CU weight for ${method}`);
}
return BigInt(row.cu_weight);
}
const headWeight = weight("eth_blockNumber");
const logsWeight = weight("eth_getLogs");
type RpcBody = {
result?: unknown;
error?: { code: number; data?: { retryable?: boolean } };
};
async function rpc(method: string, params: unknown[]): Promise<unknown> {
for (let attempt = 0; attempt < 4; attempt++) {
const response = await fetch(rpcUrl, {
method: "POST",
redirect: "error",
headers: { "Content-Type": "application/json", "x-api-key": apiKey! },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
signal: AbortSignal.timeout(15_000),
});
const body = await response.json() as RpcBody;
if (response.ok && !body.error && body.result !== undefined) return body.result;
if (body.error?.data?.retryable !== true || attempt === 3) {
throw new Error(`RPC failed: HTTP ${response.status}, code ${body.error?.code ?? "unknown"}`);
}
const retryAfter = response.headers.get("Retry-After");
const delay = retryAfter === null ? 0 : /^\d+$/.test(retryAfter)
? Number(retryAfter) * 1000 : Date.parse(retryAfter) - Date.now();
if (delay > 30_000) throw new Error("Retry-After exceeds 30 seconds; rerun later");
const backoff = 1000 * 2 ** attempt + Math.random() * 250;
await new Promise((resolve) => setTimeout(resolve, Math.max(backoff, Number.isFinite(delay) ? delay : 0)));
}
throw new Error("Retry limit reached");
}
function block(value: string): bigint {
if (!/^(0x[0-9a-f]+|[0-9]+)$/i.test(value)) throw new Error("Invalid block number");
return BigInt(value);
}
const head = await rpc("eth_blockNumber", []);
if (typeof head !== "string" || !/^0x[0-9a-f]+$/i.test(head)) throw new Error("Invalid chain head");
const latest = block(head);
const end = toBlock ? block(toBlock) : latest;
const start = fromBlock ? block(fromBlock)
: end >= maxRange - 1n ? end - maxRange + 1n : 0n;
if (start > end || end > latest) throw new Error("Require 0 <= FROM_BLOCK <= TO_BLOCK <= latest");
const chunks = (end - start + maxRange) / maxRange;
console.error(`Window ${start}..${end}; chunks=${chunks}; estimated CU=${headWeight + chunks * logsWeight}`);
const hex = (value: bigint) => `0x${value.toString(16)}`;
for (let from = start; from <= end; from += maxRange) {
const to = from + maxRange - 1n < end ? from + maxRange - 1n : end;
const result = await rpc("eth_getLogs", [{ address, fromBlock: hex(from), toBlock: hex(to) }]);
if (!Array.isArray(result)) throw new Error("Expected eth_getLogs result array");
console.log(JSON.stringify({ fromBlock: hex(from), toBlock: hex(to), result }));
}請求按順序執行。僅當 JSON-RPC 錯誤中的 error.data.retryable 為 true 時重試,每個請求最多嘗試四次,使用指數退避與抖動,並讀取 Retry-After 的秒數或 HTTP 日期。等待超過 30 秒時指令碼停止,可稍後重新執行。網路失敗、逾時、回應格式異常及不可重試錯誤立即停止;指令碼以失敗狀態退出,不報告完整回填成功。
標準輸出的每一行包含一個區段的 fromBlock、toBlock 與 result 陣列。result: [] 表示該段沒有相符的日誌。每條日誌可讀取以下欄位:
| 欄位 | 意義 |
|---|---|
address | 發出事件的合約地址。 |
blockNumber、blockHash | 日誌所在區塊,區塊編號為十六進位。 |
transactionHash、transactionIndex、logIndex | 交易與日誌位置,索引為十六進位。 |
topics、data | 事件的索引參數與 ABI 編碼的非索引參數,需使用合約 ABI 解碼。 |
removed | 日誌是否因鏈重組而被移除。 |
最新區塊不代表已最終確認。需要穩定的歷史區間時,應選用應用程式認可的已確認 TO_BLOCK,並處理鏈重組。
若區間包含 B = TO_BLOCK − FROM_BLOCK + 1 個區塊,公開單次上限為 L,區段數就是 N = ceil(B / L)。從 GET /v1/plans 的 method_weights[].cu_weight 讀取 eth_getLogs 與 eth_blockNumber 權重。指令碼向標準錯誤輸出估算值:N × weight(eth_getLogs) + weight(eth_blockNumber),其中包含指令碼攜帶 key 查詢最新區塊的一次呼叫。估算不含額外呼叫及可能計費的重試,實際結算見計費規則。CU 按呼叫估算,不按回傳日誌條數計算。
事件推送: 使用下方分段 HTTP 輪詢,或按 Webhook 推送指南將監聽地址的事件發送到 HTTPS 接收端。GET /v1/push/chains 查看支援的鏈及確認數參數,請求攜帶 x-api-key。簽章校驗、去重與重放見該指南。Webhook 推送與 WebSocket 訂閱是獨立入口,後者按 /v1/chains 的 ws 與 subscriptions 判斷。
使用 viem 或 ethers 連線
| 參數 / 端點 | 取值 / 範本 | 驗證方式 |
|---|---|---|
| Chain ID(EIP-155) | 999 | — |
| JSON-RPC(路徑攜帶 Key) | POST https://api.blockvectra.com/v1/hyperevm_mainnet/{api_key} | URL 路徑中傳入 API key |
| JSON-RPC(請求標頭攜帶 Key) | POST https://api.blockvectra.com/v1/hyperevm_mainnet | 傳入 x-api-key: {api_key} 請求標頭 |
| Data API 基址 | GET https://api.blockvectra.com/v1/data/hyperevm_mainnet/… | 傳入 x-api-key: {api_key} 請求標頭 |
| 公開狀態端點 | GET https://api.blockvectra.com/v1/status | 無需驗證(公開) |
開發者與 AI Agent 可使用同一份伺服器端參數。使用 Node.js 24 或更新版本、viem 2 或 ethers 6,先使用公開讀取;呼叫帶 key 方法時,透過環境變數安全設定 BLOCKVECTRA_API_KEY。切勿將 key 或帶 key 的 RPC URL 放入瀏覽器程式碼、日誌或版本控制中。
儲存為 network.mjs。指令碼從 GET /v1/chains 讀取 chain_id 與方法策略。免 key 讀取使用目錄中的 public.url,只呼叫 public.methods 列出的方法;公開 HTTP 可用不代表 WebSocket 可用。
const chainSlug = process.env.BLOCKVECTRA_CHAIN ?? 'hyperevm_mainnet';
const key = process.env.BLOCKVECTRA_API_KEY;
const catalogUrl = 'https://api.blockvectra.com/v1/chains';
const response = await fetch(catalogUrl, { signal: AbortSignal.timeout(15_000) });
if (!response.ok) throw new Error(`Chains HTTP ${response.status}`);
const catalog = await response.json();
export const chainInfo = catalog.chains.find(item => item.chain === chainSlug);
if (!chainInfo || !Number.isSafeInteger(chainInfo.chain_id) || chainInfo.chain_id <= 0) {
throw new Error('Missing chain or chain_id');
}
export function allows(method) {
const matches = pattern => pattern.endsWith('*')
? method.startsWith(pattern.slice(0, -1)) : pattern === method;
if (!key) return (chainInfo.public?.methods ?? []).some(matches);
return (chainInfo.methods?.allow ?? []).some(matches)
&& !(chainInfo.methods?.deny ?? []).some(matches);
}
if (!allows('eth_chainId')) throw new Error('eth_chainId is unavailable');
export const rpcUrl = key
? new URL(`./${chainSlug}/${encodeURIComponent(key)}`, catalogUrl).href
: chainInfo.public?.url;
if (!rpcUrl) throw new Error('Public RPC is unavailable; set BLOCKVECTRA_API_KEY');儲存為 viem-client.mjs,透過 npm install viem@2 安裝後執行 node viem-client.mjs。
import { createPublicClient, defineChain, http } from 'viem';
import { chainInfo, rpcUrl } from './network.mjs';
export const chain = defineChain({
id: chainInfo.chain_id,
name: chainInfo.name,
nativeCurrency: { name: 'HYPE', symbol: 'HYPE', decimals: 18 },
rpcUrls: { default: { http: [rpcUrl] } },
});
export const client = createPublicClient({ chain, transport: http(rpcUrl) });
if (await client.getChainId() !== chain.id) throw new Error('RPC chain ID mismatch');
console.error(await client.getBlockNumber());使用 ethers 時,儲存為 ethers-client.mjs,透過 npm install ethers@6 安裝後執行 node ethers-client.mjs。
import { JsonRpcProvider } from 'ethers';
import { chainInfo, rpcUrl } from './network.mjs';
const provider = new JsonRpcProvider(rpcUrl, chainInfo.chain_id, { batchMaxCount: 1 });
const network = await provider.getNetwork();
if (network.chainId !== BigInt(chainInfo.chain_id)) throw new Error('RPC chain ID mismatch');
console.log(await provider.getBlockNumber());
provider.destroy();使用 Foundry 或 Hardhat 部署
目前 hyperevm_mainnet 目錄為 ws=false,methods.allow 未列出 eth_sendRawTransaction。BlockVectra 用於讀取;部署需要支援廣播的 RPC。將 DEPLOY_RPC_URL 設定為該服務商的帶驗證 HTTP URL,不要套用 BlockVectra 的方法或日誌跨度限額。簽名前核對目標 chain ID。
: "${DEPLOY_RPC_URL:?Set a broadcasting RPC URL}"
export RPC_URL="$DEPLOY_RPC_URL"
export CHAIN_ID="$(node --input-type=module -e "import { chainInfo } from './network.mjs'; console.log(chainInfo.chain_id)")"接著按共用的 Foundry 或 Hardhat 部署教學部署。部署地址需持有 EVM HYPE;部署大型合約前先檢查下方雙區塊要求。
HYPE、小區塊與大型合約部署
HyperEVM 官方網路指南說明 gas 使用 HYPE,精度為 18 位(存取日期:2026-10-07)。部署地址需要在 HyperEVM 上持有 HYPE,HyperCore 餘額不能直接當作 EVM gas 餘額。轉入資金時按該頁連結的原生資產轉移說明操作。
雙區塊架構指南說明小區塊出塊快,大區塊出塊較慢、用於容納較大交易(存取日期:2026-10-07)。先估算部署 gas;超過小區塊預算時,部署地址需先成為已有 HyperCore 使用者,再簽名提交 Core action {"type":"evmUserModify","usingBigBlocks":true}。只調大交易 gas limit 不會選擇大區塊。完成後復原 usingBigBlocks=false,返回小區塊模式。
在支援這些方法的服務商上,用 eth_usingBigBlocks 檢查地址模式,用 eth_bigBlockGasPrice 取得大區塊基礎費用。官方 JSON-RPC 參考說明了這兩個方法(存取日期:2026-10-07)。核對所選服務商的方法;BlockVectra 能力以 /v1/chains 為準。上方最小部署面向小型合約,不會變更 Core 帳戶模式。
HyperCore 與 HyperEVM 資料
EVM RPC 用於合約、收據(receipt)與日誌;HyperCore 交易資料和操作使用 Core API。合約可透過預編譯讀取 Core 狀態,透過 CoreWriter 發送操作,接入時參考官方互動指南(存取日期:2026-10-07)。EVM 日誌不能替代 Core 訂單簿或倉位查詢。
HyperEVM 的系統交易(例如 HyperCore 到 HyperEVM 的轉帳)不在標準 eth_getBlockByNumber 回傳中,官方 RPC 透過 eth_getSystemTxsByBlockNumber 與 eth_getSystemTxsByBlockHash 單獨提供(見官方 JSON-RPC 文件,存取日期:2026-10-07)。BlockVectra 目前的 HyperEVM 區塊、交易與 Data API 資料不包含系統交易,需要此類資料時直接使用官方 RPC 的這兩個方法。
處理官方錯誤 10055
HyperEVM 官方指南將 10055 定義為 Core/EVM 邊界錯誤,包括 nonce、餘額不足、重複雜湊與替換交易費用不足(存取日期:2026-10-07)。先讀取廣播 RPC 的錯誤訊息,再決定復原動作:
- Nonce: 對照
eth_getTransactionCount與待處理交易,同一部署地址循序提交,並核對下一個 nonce。 - 餘額: 檢查部署地址的 EVM HYPE 是否足夠支付轉帳金額與 gas。
- 重複雜湊: 先查詢既有交易和收據,再決定是否提交其他交易。
- 替換費用: 核對既有 nonce 與費用,按廣播服務商的替換策略提高費用;重複發送相同位元組不會提高費用。
不能僅憑 10055 盲目重試。錯誤與其復原指引另見 BlockVectra 錯誤參考。
官方公共 RPC 限流與 429
Hyperliquid 官方的限流說明規定,rpc.hyperliquid.xyz/evm 每個 IP 每分鐘最多 100 次 EVM JSON-RPC 請求;其 JSON-RPC 文件還規定,eth_getLogs 單次最多查詢 50 個區塊、最多 4 個 topics。存取日期:2026-10-07。
遇到 HTTP 429 時暫停請求,優先遵循 Retry-After(秒數或 HTTP 日期);沒有該回應標頭時採用有次數上限、帶隨機抖動的指數退避,重試同一個尚未成功的區段。降低並行與輪詢頻率,將日誌查詢拆成符合端點上限的小段;分段本身不會消除速率限制,多個用戶端共用一個 IP 時還需協調請求頻率。
使用 BlockVectra 的帶 key 端點時,從 GET /v1/chains 讀取 hyperevm_mainnet 的 max_logs_block_range、methods.allow 與 methods.deny,不要套用官方公共 RPC 的區塊跨度或每分鐘請求數。請求速率另受 key 的 cu_per_sec、burst_cu 與免費方案呼叫頻率約束(見下節);429 時按 error.data.reason 與 retryable 處理,request_exceeds_burst 需要拆小請求,不能原樣退避重試。
BlockVectra 參數與服務規則
BlockVectra 為 HyperEVM 主網提供 JSON-RPC 與 REST 風格的 Data API:
- 鏈參數與日誌範圍:
根據公開介面
GET /v1/chains中hyperevm_mainnet的記錄:- 鏈識別代號(Slug):
hyperevm_mainnet,Chain ID 為999。 max_logs_block_range:以GET /v1/chains的max_logs_block_range欄位為準。單次eth_getLogs請求的區塊跨度(toBlock − fromBlock + 1)不能超過該上限。若跨度超出上限,平台回傳 HTTP 200 與 JSON-RPC 錯誤碼-32602(eth_getLogs block range too large: max <N> blocks),該錯誤不計費。state_window_blocks:以GET /v1/chains的state_window_blocks欄位為準。狀態讀取方法(如eth_call、eth_getBalance)受該欄位宣告的保留區塊範圍約束(為null時表示全量保留,不設短期滾動範圍限制)。- 方法策略:以
methods.allow與methods.deny欄位為準。常用讀取方法(eth_blockNumber、eth_getLogs、eth_call、eth_getBalance、eth_getBlockByNumber、eth_getTransactionReceipt等)均開放;過濾和訂閱方法(eth_subscribe、eth_unsubscribe、eth_newFilter、eth_newBlockFilter)已停用,呼叫停用方法回傳-32601(不計費)。
- 鏈識別代號(Slug):
- 免費方案速率限制與升級:
根據
GET /v1/plans的資料:free.max_calls_per_sec:上限 25 次/秒,帳戶內所有 key、所有鏈和 Data API 共享。- 單 key 預設限制:每個 API key 均有 CU 權杖桶(
cu_per_sec補充速率、burst_cu突發容量——預設 400 CU/s、突發 1,600 CU)。方法按 CU 權重計費。 - 限額提升:首次付費儲值後,解除帳戶級每秒呼叫上限;每個 key 仍有預設 CU 速率與突發上限。關於目前費率與計費規則,請參閱定價頁。
回填歷史資料:分段 eth_getLogs 與重試判斷
回填歷史事件日誌時,需要將大跨度區間按目標鏈的 max_logs_block_range 上限分塊請求。用戶端的重試策略應檢查錯誤回應中的 retryable 欄位。
錯誤主體中的 retryable 判定
在 BlockVectra 平台上,JSON-RPC 錯誤回應的 error.data 包含 reason、docs_url 以及 retryable(布林值):
retryable: true(可重試):例如瞬時過載(overloaded)、免費方案每秒頻次超限(free_plan_call_limit)、節點同步中(node_syncing)或上游服務無法使用(upstream_unavailable)。若 HTTP 回應標頭帶有Retry-After,應遵循其指定的秒數或 HTTP 日期;否則採用帶抖動的指數退避重試。retryable: false(不可重試):例如區塊跨度超限(-32602/logs_range_too_large)、請求參數錯誤(invalid_params)、缺少 API key(missing_api_key)或突發容量超限(-32022/request_exceeds_burst)。此類錯誤盲目重試無法成功,必須修正參數後再發起請求。
未帶 API key 時的真實 401 回應主體結構如下:
{
"jsonrpc": "2.0",
"id": null,
"error": {
"code": -32024,
"message": "missing API key: send it in the request path (/v1/{chain}/<api_key>) or in the x-api-key header",
"data": {
"reason": "missing_api_key",
"docs_url": "https://docs.blockvectra.com/en/errors/#missing_api_key",
"retryable": false
}
}
}使用 Data API 端點取代大範圍 getLogs 掃描
當應用程式需要追蹤某個特定地址的交易歷史或代幣轉移時,透過 eth_getLogs 掃描需要按目標鏈的 max_logs_block_range 逐段發起請求,並解析原始 Transfer 事件日誌。
BlockVectra 的 Data API 針對 hyperevm_mainnet 提供了按地址索引的專用端點,單次查詢可涵蓋最寬 100,000 個區塊並支援游標分頁:
- 地址交易清單:
GET /v1/data/hyperevm_mainnet/addresses/{address}/transactions- 查詢參數:
from_block(必填)、to_block(必填)、direction(選填:from,to,any,預設any)、clamp(選填的布林字串,預設為false;設為true時,區間超出 100,000 塊或高於as_of_block會自動截斷,避免回傳 409 錯誤)、limit(選填,最大 500)、cursor(分頁游標)。
- 查詢參數:
- 地址代幣轉帳清單:
GET /v1/data/hyperevm_mainnet/addresses/{address}/transfers- 查詢參數:
standard(必填:erc20或erc721;erc1155不支援按地址查詢並回傳422 no_coverage)、token(選填合約地址)、from_block(必填)、to_block(必填)、direction(選填:in,out,any)、clamp(選填)、limit、cursor。
- 查詢參數:
回應結構說明
兩個端點均使用標準回應結構:
data:資料物件陣列。地址交易包含hash、block_number、block_timestamp、from、to、value、tx_index、gas_limit、gas_used、status等;代幣轉帳包含token、standard、from、to、block_number、block_timestamp、tx_hash、tx_index、log_index(ERC-20 包含amount,ERC-721 包含token_id)。next_cursor:存在下一頁資料時回傳的不透明游標字串;若為最後一頁則該欄位不存在(非null)。meta:包含chain、chain_slug、chain_external_id、as_of_block、safe_block、finalized_block、coverage(full或partial)以及refreshed_at。
程式碼範例:呼叫 Data API
export BLOCKVECTRA_API_KEY="rgw_your_api_key"
# 1. 查詢地址歷史交易(支援 clamp=true 避免 409)
curl -s "https://api.blockvectra.com/v1/data/hyperevm_mainnet/addresses/0x2222222222222222222222222222222222222222/transactions?from_block=0&to_block=50000&clamp=true" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"
# 2. 查詢地址 ERC-20 轉帳記錄
curl -s "https://api.blockvectra.com/v1/data/hyperevm_mainnet/addresses/0x2222222222222222222222222222222222222222/transfers?standard=erc20&from_block=0&to_block=50000&clamp=true" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"即時追蹤:輪詢新區塊
使用 HTTP transport 時輪詢新區塊,在 max_logs_block_range 內連續分段讀取事件日誌。僅當 /v1/chains 回傳 ws=true 且 subscriptions 包含所需類型時選擇 WebSocket。推送至 HTTPS 接收端可使用 Webhook 推送。
驗證已部署的 Hello 合約時,將 LOG_ADDRESS 設為其地址。經廣播 RPC 發送 ping(),再用本頁分段指令碼回填收據所在區塊;後續事件從最後成功區段繼續讀取。
cast send "$CONTRACT_ADDRESS" "ping()" --rpc-url "$DEPLOY_RPC_URL" \
--private-key "$DEPLOYER_PRIVATE_KEY"輪詢實作邏輯
- 定時呼叫輕量讀取方法
eth_blockNumber取得鏈上最新高度。 - 比較目前高度與已處理高度
lastSeenBlock。 - 若
currentBlock > lastSeenBlock,將[lastSeenBlock + 1, currentBlock]拆成最多max_logs_block_range的區段。每段處理成功後才持久化lastSeenBlock;失敗時重試未完成區段。按(blockHash, transactionHash, logIndex)去重,重新連線時回掃重疊區間以核對重組。 - viem 的
watchBlockNumber或watchBlocks在 HTTP transport 下原生使用輪詢機制,可透過pollingInterval參數自訂輪詢週期(例如 1000 毫秒)。
分段輪詢事件日誌
儲存為 poll-logs.mjs,與 network.mjs、viem-client.mjs 放在一起。設定 BLOCKVECTRA_API_KEY、LOG_ADDRESS 與 FROM_BLOCK 後執行 node poll-logs.mjs。這個有限範例輪詢鏈頭 12 次,間隔五秒,將每個新區間連續分段查詢;錯誤會停止指令碼,不推進失敗區段。
import { isAddress } from 'viem';
import { client } from './viem-client.mjs';
import { chainInfo, allows } from './network.mjs';
const address = process.env.LOG_ADDRESS;
const start = process.env.FROM_BLOCK;
if (!address || !isAddress(address)) throw new Error('Set LOG_ADDRESS');
if (!/^(0x[0-9a-f]+|[0-9]+)$/i.test(start ?? '')) throw new Error('Set FROM_BLOCK');
if (!allows('eth_getLogs')) throw new Error('eth_getLogs requires an available keyed endpoint');
if (!Number.isSafeInteger(chainInfo.max_logs_block_range) || chainInfo.max_logs_block_range <= 0) {
throw new Error('Invalid max_logs_block_range');
}
const max = BigInt(chainInfo.max_logs_block_range);
let from = BigInt(start);
for (let poll = 0; poll < 12; poll++) {
const head = await client.getBlockNumber();
while (from <= head) {
const to = from + max - 1n < head ? from + max - 1n : head;
const logs = await client.getLogs({ address, fromBlock: from, toBlock: to });
console.log(JSON.stringify({ from: from.toString(), to: to.toString(), logs },
(_, value) => typeof value === 'bigint' ? value.toString() : value));
from = to + 1n;
}
if (poll < 11) await new Promise(resolve => setTimeout(resolve, 5_000));
}每條輸出記錄一個已完成區段,繼續執行時將 FROM_BLOCK 設為其 to + 1。持久化取用端需一併儲存事件與游標、去重,並按上方步驟核對重組。429 或其他可重試錯誤按有界退避說明處理同一個未完成區段。
相關指南
- 在 HyperEVM 鏈頁面尋找公開 RPC URL、支援的方法與目前限制。
- 有關
eth_getLogs跨度與切塊演算法的完整規則,請參閱 eth_getLogs 區塊範圍限制與分段查詢。 - 有關將
eth_getLogs與 Data API 轉帳進行比較、理解as_of_block邊界以及safe_block/finalized_block標記,請參閱 eth_getLogs 與已索引轉帳:覆蓋範圍與最終性。 - 有關 CU 計量、不計費錯誤與重試的詳細資訊,請參閱哪些情況不扣費:錯誤碼與計費規則。
下一步
最後更新: