API số dư token trong ví: tài sản ERC-20 và lịch sử chuyển tiền
Xây dựng trang tài sản ví với số dư token ERC-20 khác 0, lịch sử chuyển token và siêu dữ liệu theo lô. Kiểm tra phạm vi chuỗi, phân trang kết quả và quy đổi số tiền nguyên theo decimals.
Xây dựng trang tài sản ví bằng API dữ liệu ví blockchain: dùng Token Balances API cho lượng ERC-20 nắm giữ khác 0 và Token Transfers API cho lịch sử ví. Nhà phát triển và AI Agent dùng cùng các yêu cầu có xác thực. Trước khi truy vấn, đọc GET /v1/status và kiểm tra data_features cùng data_status của chuỗi đã chọn; phạm vi số dư khác nhau theo chuỗi. Tham số yêu cầu và lược đồ phản hồi nằm trong tham chiếu Data API.
Các tác vụ hướng dẫn này giúp bạn hoàn thành
- Đọc số dư token trong ví bằng key và phân trang lượng ERC-20 nắm giữ khác 0.
- Đọc lịch sử chuyển tiền của ví trong cửa sổ khối cố định và theo con trỏ cho địa chỉ đã chọn.
- Bổ sung siêu dữ liệu token để hiển thị tên và ký hiệu bên cạnh số dư nguyên thô, giữ nguyên các trường thiếu.
Ba loại dữ liệu trang tài sản ví cần
Trang tài sản ví có thể hiển thị số dư token ERC-20, lịch sử chuyển token và siêu dữ liệu token của một địa chỉ. Data API cung cấp endpoint cho từng loại:
- Số dư:
GET /{chain}/addresses/{address}/balancestrả số dư ERC-20 khác 0 của địa chỉ, sắp xếp tăng dần theo địa chỉtoken, kèmsymbolvàdecimalscủa token khi có. Địa chỉ không có số dư trả200vớidata: []. - Chuyển tiền:
GET /{chain}/addresses/{address}/transferstrả các chuyển token liên quan tới địa chỉ trong cửa sổ khối bắt buộc, sắp xếp giảm dần theo(block_number, log_index). - Siêu dữ liệu token:
GET /{chain}/tokens/{token}đọc tên, ký hiệu, số chữ số thập phân và tổng cung của một token theo địa chỉ hợp đồng;POST /{chain}/tokens:batchđọc cùng siêu dữ liệu cho tối đa 100 địa chỉ trong một yêu cầu.
Cả ba dùng https://api.blockvectra.com/v1/data làm URL cơ sở và header yêu cầu x-api-key, với robinhood_mainnet là chuỗi ví dụ. Chúng lần lượt thuộc các khả năng balances, transfers và token_metadata; để xem chuỗi cung cấp từng khả năng, đọc trang Chuỗi được hỗ trợ. Trên chuỗi không có khả năng tương ứng, endpoint trả 422 no_coverage.
Yêu cầu 1: số dư địa chỉ
Endpoint này cần ít tham số hơn, nên phù hợp làm yêu cầu đầu tiên cho trang:
{chain}(tham số đường dẫn, bắt buộc): định danh chuỗi, là giá trịchaincủa một mục trongGET /chains(ví dụrobinhood_mainnet). Khớp chính xác và phân biệt chữ hoa, chữ thường; không chấp nhận bí danh hoặc Chain ID dạng số.{address}(tham số đường dẫn, bắt buộc): địa chỉ 20 byte; tiền tố0xlà tùy chọn và chấp nhận cả chữ hoa lẫn chữ thường.limit(tham số truy vấn, tùy chọn): kích thước trang. Mặc định 50; giá trị trên 500 được giới hạn thành 500;0hoặc giá trị không nguyên trả400 bad_request.cursor(tham số truy vấn, tùy chọn):next_cursorcủa phản hồi trước, truyền lại nguyên vẹn để lấy trang tiếp theo. Con trỏ chỉ hợp lệ cho chuỗi, endpoint và tham số truy vấn đã tạo ra nó; dùng lại ở nơi khác trả400 bad_request.
Endpoint phân trang theo khóa: next_cursor chỉ xuất hiện khi có trang tiếp theo. Ở trang cuối, khóa hoàn toàn không có, tuyệt đối không phải null.
export BLOCKVECTRA_API_KEY=rgw_your_api_key
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/balances?limit=50" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"Cấu trúc phản hồi là AddressBalanceListEnvelope, chứa data và meta. Mỗi mục trong data là một AddressBalance:
| Trường | Kiểu | Mô tả |
|---|---|---|
token | string (địa chỉ) | Địa chỉ hợp đồng token; dạng chuẩn là 0x cộng 40 chữ số hex viết thường. |
balance | string (thập phân) | Số dư nguyên thô, có thể vượt 2^53, trả dưới dạng chuỗi thập phân thuần — tuyệt đối không phải số JSON, ký pháp khoa học hay hex. |
symbol | string hoặc null | Ký hiệu token, hoặc null khi không có. |
decimals | integer hoặc null | Số chữ số thập phân của token, 0–255, hoặc null khi không có. |
Yêu cầu 2: chuyển tiền của địa chỉ
Endpoint chuyển tiền yêu cầu cửa sổ khối tường minh: cả from_block và to_block đều bắt buộc và phải thỏa from_block <= to_block. Endpoint cần thêm vài tham số:
standard(tham số truy vấn, bắt buộc):erc20hoặcerc721. Truy vấn theo địa chỉ không bao phủerc1155; truyền giá trị đó trả422 no_coverage.direction(tham số truy vấn, tùy chọn):in,outhoặcany; mặc địnhanyvà lọc theo hướng tương đối với địa chỉ.token(tham số truy vấn, tùy chọn): giới hạn kết quả ở một hợp đồng token.clamp(tham số truy vấn, tùy chọn): chỉ chuỗi nguyên văntruemới bật tùy chọn này; mọi giá trị khác được coi làfalse.
Giới hạn cửa sổ và tính chung cuộc: to_block tường minh vượt as_of_block trả 409 not_indexed_yet, trừ khi clamp=true cắt xuống as_of_block; cửa sổ rộng hơn giới hạn của chuỗi (limits.max_window_blocks từ GET /chains) trả 409 window_too_large, trừ khi clamp=true cắt từ đầu cũ hơn (tăng from_block và giữ nguyên to_block). Nếu bản thân from_block đã vượt as_of_block, vẫn trả lỗi 409 ngay cả với clamp=true. Khi cửa sổ bị cắt hoặc chỉ được bao phủ một phần, meta.coverage trong phản hồi là "partial"; nếu không là "full".
Trong bản ghi chuyển tiền, mục ERC-20 thêm amount; mục ERC-721 thêm token_id. Cả hai đều có token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index và log_index.
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# 1) Read as_of_block from the balances response.
AS_OF_BLOCK=$(curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/balances" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" | grep -o '"as_of_block":[0-9]*' | cut -d: -f2)
# 2) clamp=true truncates a too-wide window, or a to_block above as_of_block, instead of returning 409.
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/transfers?standard=erc20&from_block=0&to_block=$AS_OF_BLOCK&direction=any&clamp=true" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"Phân trang qua toàn bộ chuyển tiền
next_cursor của endpoint chuyển tiền theo địa chỉ mang tính dự đoán: chỉ xuất hiện khi trang trả đúng limit hàng, nên trang có thể có next_cursor nhưng vẫn là trang cuối. Đừng dừng khi trang rỗng; theo next_cursor cho đến khi khóa không còn.
Mã bên dưới lấy mọi chuyển tiền trong cửa sổ:
const address = "0x1111111111111111111111111111111111111111";
const head = await fetch(
`https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/balances`,
{ headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! } },
).then((r) => r.json());
const asOfBlock = head.meta.as_of_block;
const transfers: unknown[] = [];
let cursor: string | undefined;
do {
const url = new URL(
`https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/transfers`,
);
url.searchParams.set("standard", "erc20");
url.searchParams.set("from_block", "0");
url.searchParams.set("to_block", String(asOfBlock));
url.searchParams.set("limit", "500");
// clamp truncates from the older end
url.searchParams.set("clamp", "true");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
});
const page = await res.json();
transfers.push(...page.data);
cursor = page.next_cursor; // absent on the last page
} while (cursor);Yêu cầu 3: siêu dữ liệu token và tokens:batch
Đọc một token bằng GET /{chain}/tokens/{token}; đường dẫn chỉ nhận {chain} và {token}, không phân trang. Cấu trúc phản hồi là TokenEnvelope, và data là một Token:
| Trường | Kiểu | Mô tả |
|---|---|---|
address | string (địa chỉ) | Địa chỉ hợp đồng token. |
standard | string | erc20, erc721 hoặc unknown. |
name | string hoặc null | Tên token, hoặc null khi không có. |
symbol | string hoặc null | Ký hiệu token, hoặc null khi không có. |
decimals | integer hoặc null | Số chữ số thập phân của token, 0–255, hoặc null khi không có. |
total_supply | string hoặc null | Tổng cung thô; API không quy đổi theo decimals. null khi không có. |
first_seen_block | integer (int64) | Độ cao khối nơi token được thấy lần đầu. |
metadata_updated_at | string (dấu thời gian) | Thời gian UTC siêu dữ liệu được cập nhật lần cuối. |
metadata_block | integer (int64) | Độ cao khối tại đó siêu dữ liệu được đọc. |
metadata_status | string | ok, partial hoặc unavailable. |
metadata_issues | object | Bản ghi vấn đề theo từng trường, với khóa name, symbol, decimals, total_supply và giá trị reverted, no_data, invalid_encoding hoặc temporarily_unavailable. |
{token} không phải địa chỉ 20 byte hợp lệ trả 400 bad_request; {token} không được biết đến trả 404 not_found; {chain} không được biết đến trả 404 unknown_chain.
Endpoint số dư đã bao gồm symbol và decimals khi có, nhưng cả hai có thể là null. Để bổ sung tên và số chữ số thập phân cho mọi token trong ví, dùng POST /{chain}/tokens:batch:
- Body yêu cầu là
{"addresses": [...]}với tối đa 100 địa chỉ mỗi yêu cầu; hơn 100 mục, hoặc một mục không phải địa chỉ 20 byte hợp lệ, trả400 bad_request(thất bại tại địa chỉ không hợp lệ đầu tiên được duyệt tới). - Địa chỉ không tìm thấy không gây lỗi; chúng nằm trong
data.missing, còndata.tokenschỉ chứa token có siêu dữ liệu tìm thấy. - Địa chỉ trùng được loại trùng trong cả
tokensvàmissing, mỗi danh sách theo thứ tự xuất hiện đầu tiên trong yêu cầu.
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# Single token
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"
# Batch: up to 100 addresses per request
curl -s -X POST "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens:batch" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"addresses":["0x1111111111111111111111111111111111111111","0x2222222222222222222222222222222222222222"]}'Quy đổi số tiền theo decimals
Trường số dư balance và trường chuyển tiền ERC-20 amount là số nguyên thô biểu diễn bằng chuỗi thập phân (UInt256String); total_supply của token cũng là số nguyên thô trên chuỗi chưa quy đổi theo decimals. Để hiển thị số lượng dễ đọc, quy đổi theo decimals của token đó.
decimalslấy từsymbol/decimalscủa chính mục số dư, hoặc từGET /{chain}/tokens/{token}vàPOST /{chain}/tokens:batch; có thể lànull.- Các giá trị này có thể vượt
2^53, nên đừng tính toán bằng số JSON: dùngBigInttrong TypeScript vàDecimaltrong Python, phân tích nguyên chuỗi thập phân để tránh mất độ chính xác.
function toDisplayAmount(raw: string, decimals: number | null): string {
if (decimals === null) return raw; // no decimals metadata: keep the raw integer
const value = BigInt(raw);
const base = 10n ** BigInt(decimals);
const whole = value / base;
const fraction = (value % base)
.toString()
.padStart(decimals, "0")
.replace(/0+$/, "");
return fraction ? `${whole}.${fraction}` : whole.toString();
}
// balance.balance is a raw decimal string; decimals comes from the same item or tokens:batch.
const display = toDisplayAmount(balance.balance, balance.decimals);Độ mới của dữ liệu
Mọi phản hồi thành công trong phạm vi chuỗi đều có meta:
as_of_block: khối mới nhất của chuỗi đã được ghi đầy đủ. Endpoint theo khối phục vụ dữ liệu đến độ cao này.safe_block: mốc chỉ ra block tag đồng thuậnsafecủa nút (nullkhi chưa biết). Không bao giờ thấp hơnfinalized_block, và không cắt, từ chối hoặc trì hoãn phản hồi.finalized_block: mốc chỉ ra block tag đồng thuậnfinalizedcủa nút (nullkhi chưa biết). Không cắt, từ chối hoặc trì hoãn phản hồi; client quyết định mức an toàn cần từ mốc đó (chẳng hạn trạng thái xác nhận).coverage:"full"hoặc"partial". Chuyển tiền theo địa chỉ và endpoint tương tự báo"partial"khiclampthu hẹp cửa sổ được phục vụ, hoặc khi cửa sổ bắt đầu trước khối đầu tiên được lập chỉ mục của chuỗi.refreshed_at: thời điểm dữ liệu của phản hồi được cập nhật lần cuối (UTC). Có thể lànull:nullnghĩa là chưa biết thời gian cập nhật dữ liệu và cần coi dữ liệu là cũ; endpoint theo khối luôn trả giá trị.- Phản hồi cũng lặp lại
chain,chain_slugvàchain_external_id.
Cách dùng phổ biến: đọc meta.as_of_block từ phản hồi đầu tiên bất kỳ để đọc đến khối mới nhất đã lập chỉ mục, và kiểm tra meta.safe_block / meta.finalized_block nếu muốn hiển thị trạng thái đã xác nhận.
Ước tính CU cho một lần tải trang
Mỗi phương thức được tính phí theo trọng số CU, đọc từ API gói của nền tảng:
Trọng số CU mỗi lệnh gọi
| Phương thức | CU mỗi lệnh gọi |
|---|---|
data.address_balances | 25 |
data.address_transfers | 25 |
data.tokens_batch | 10 |
Một lần tải trang (ước tính)
1 yêu cầu balances + 3 trang transfer + 1 yêu cầu tokens:batch, tổng cộng 5 lệnh gọi, khoảng 110 CU. Mức sử dụng thực tế phụ thuộc vào số lượng trang và token.
Để quyết định tính phí và biết phản hồi lỗi không tính phí, xem quy tắc thanh toán. Nếu cần log từ các khối mới nhất thay vì lịch sử chuyển tiền được lập chỉ mục, đọc Dữ liệu nút gần đây và lịch sử được lập chỉ mục trước khi quyết định chuyển sang eth_getLogs.
Các bước tiếp theo
- Duyệt danh mục bộ dữ liệu để xem mọi bộ dữ liệu BlockVectra lập chỉ mục.
- Xem gói miễn phí và giá để kiểm tra những gì tài khoản bao gồm.
- Đăng nhập bảng điều khiển để tạo API key.
Cập nhật lần cuối:
Cổ phiếu token hóa
Truy vấn bảng xếp hạng cổ phiếu token hóa hàng ngày và các chỉ số lịch sử bằng Data API, bao gồm các trường, quy ước mã hóa, phân trang và ước tính mức sử dụng.
RPC tùy chỉnh cho ví
Thêm URL RPC BlockVectra vào MetaMask hoặc Rabby. Tìm Chain ID và ký hiệu tài sản gốc, cấu hình API key trong đường dẫn và quản lý key riêng cho ví.