API saldo token dompet: aset ERC-20 dan riwayat transfer

Bangun halaman aset dompet dengan saldo token ERC-20 non-nol, riwayat transfer token, dan metadata batch. Periksa cakupan chain, gunakan paginasi, dan skalakan jumlah bilangan bulat berdasarkan decimals.

Bangun halaman aset dompet dengan API data dompet blockchain: gunakan Token Balances API untuk kepemilikan ERC-20 non-nol dan Token Transfers API untuk riwayat dompet. Pengembang dan AI Agent menggunakan permintaan terautentikasi yang sama. Sebelum melakukan kueri, baca GET /v1/status dan periksa data_features serta data_status chain yang dipilih; cakupan saldo berbeda untuk setiap chain. Parameter permintaan dan skema respons tersedia di referensi Data API.

Tugas yang dapat Anda selesaikan dengan panduan ini

Tiga jenis data yang dibutuhkan halaman aset dompet

Halaman aset dompet dapat menampilkan saldo token ERC-20, riwayat transfer token, dan metadata token suatu alamat. Data API menyediakan endpoint untuk masing-masing:

  • Saldo: GET /{chain}/addresses/{address}/balances mengembalikan saldo ERC-20 non-nol alamat tersebut, diurutkan berdasarkan alamat token secara menaik, dengan symbol dan decimals token disertakan jika tersedia. Alamat tanpa saldo mengembalikan 200 dengan data: [].
  • Transfer: GET /{chain}/addresses/{address}/transfers mengembalikan transfer token yang melibatkan alamat tersebut dalam jendela blok yang wajib ditentukan, diurutkan berdasarkan (block_number, log_index) secara menurun.
  • Metadata token: GET /{chain}/tokens/{token} membaca nama, simbol, decimals, dan total suplai satu token berdasarkan alamat kontrak; POST /{chain}/tokens:batch membaca metadata yang sama untuk hingga 100 alamat dalam satu permintaan.

Ketiganya menggunakan https://api.blockvectra.com/v1/data sebagai URL dasar dan header permintaan x-api-key, dengan robinhood_mainnet sebagai chain contoh. Masing-masing termasuk dalam kemampuan balances, transfers, dan token_metadata; untuk chain yang menyediakan setiap kemampuan, lihat halaman Chain yang Didukung. Pada chain tanpa kemampuan tersebut, endpoint mengembalikan 422 no_coverage.

Permintaan 1: saldo alamat

Endpoint ini memerlukan lebih sedikit parameter, sehingga cocok sebagai permintaan pertama untuk suatu halaman:

  • {chain} (parameter path, wajib): pengenal chain, yaitu nilai chain dari entri dalam GET /chains (misalnya robinhood_mainnet). Pencocokan harus persis dan peka huruf besar-kecil; alias dan Chain ID numerik tidak diterima.
  • {address} (parameter path, wajib): alamat 20-byte; awalan 0x opsional dan huruf besar maupun kecil diterima.
  • limit (parameter kueri, opsional): ukuran halaman. Nilai bawaan 50; nilai di atas 500 dibatasi menjadi 500; 0 atau nilai non-bilangan bulat mengembalikan 400 bad_request.
  • cursor (parameter kueri, opsional): next_cursor dari respons sebelumnya, dikirim kembali tanpa perubahan untuk mengambil halaman berikutnya. Kursor hanya berlaku untuk chain, endpoint, dan parameter kueri yang menerbitkannya; penggunaan di tempat lain mengembalikan 400 bad_request.

Paginasi menggunakan keyset: next_cursor hanya muncul jika ada halaman berikutnya. Pada halaman terakhir, kunci tersebut sama sekali tidak ada, bukan 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"

Struktur respons adalah AddressBalanceListEnvelope, yang berisi data dan meta. Setiap item data adalah AddressBalance:

BidangTipeDeskripsi
tokenstring (alamat)Alamat kontrak token; bentuk kanonisnya adalah 0x diikuti 40 digit heksadesimal huruf kecil.
balancestring (desimal)Saldo bilangan bulat mentah, yang dapat melebihi 2^53, dikembalikan sebagai string desimal biasa — bukan angka JSON, notasi ilmiah, atau heksadesimal.
symbolstring atau nullSimbol token, atau null jika tidak tersedia.
decimalsinteger atau nullDecimals token, 0–255, atau null jika tidak tersedia.

Permintaan 2: transfer alamat

Endpoint transfer memerlukan jendela blok eksplisit: from_block dan to_block keduanya wajib dan harus memenuhi from_block <= to_block. Endpoint ini menerima beberapa parameter tambahan:

  • standard (parameter kueri, wajib): erc20 atau erc721. Kueri dengan cakupan alamat tidak mencakup erc1155; mengirimkannya mengembalikan 422 no_coverage.
  • direction (parameter kueri, opsional): in, out, atau any; nilai bawaan any dan menyaring berdasarkan arah relatif terhadap alamat.
  • token (parameter kueri, opsional): membatasi hasil pada satu kontrak token.
  • clamp (parameter kueri, opsional): hanya string literal true yang mengaktifkannya; nilai lainnya dianggap false.

Batas jendela dan finalitas: to_block eksplisit di atas as_of_block mengembalikan 409 not_indexed_yet kecuali clamp=true memangkasnya ke as_of_block; jendela yang lebih lebar dari batas chain (limits.max_window_blocks dari GET /chains) mengembalikan 409 window_too_large kecuali clamp=true memangkas dari ujung yang lebih lama (menaikkan from_block dan mempertahankan to_block). Jika from_block sendiri sudah melewati as_of_block, respons tetap 409 tanpa pengecualian meskipun clamp=true. Saat jendela dipangkas atau hanya tercakup sebagian, meta.coverage respons adalah "partial"; selain itu "full".

Dalam catatan transfer, item ERC-20 menambahkan amount; item ERC-721 menambahkan token_id. Keduanya mencakup token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index, dan 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"

Menelusuri semua transfer dengan paginasi

next_cursor endpoint transfer alamat bersifat optimistis: hanya muncul ketika halaman mengembalikan tepat limit baris, sehingga halaman dapat memiliki next_cursor meskipun ternyata merupakan halaman terakhir. Jangan berhenti ketika halaman kosong; ikuti next_cursor sampai kunci tersebut tidak ada.

Kode berikut mengambil setiap transfer dalam jendela:

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);

Permintaan 3: metadata token dan tokens:batch

Baca satu token dengan GET /{chain}/tokens/{token}; path hanya menerima {chain} dan {token}, tanpa paginasi. Struktur respons adalah TokenEnvelope, dan data adalah Token:

BidangTipeDeskripsi
addressstring (alamat)Alamat kontrak token.
standardstringerc20, erc721, atau unknown.
namestring atau nullNama token, atau null jika tidak tersedia.
symbolstring atau nullSimbol token, atau null jika tidak tersedia.
decimalsinteger atau nullDecimals token, 0–255, atau null jika tidak tersedia.
total_supplystring atau nullTotal suplai mentah; API tidak menerapkan penskalaan decimals. null jika tidak tersedia.
first_seen_blockinteger (int64)Tinggi blok tempat token pertama kali terlihat.
metadata_updated_atstring (timestamp)Waktu UTC saat metadata terakhir diperbarui.
metadata_blockinteger (int64)Tinggi blok saat metadata dibaca.
metadata_statusstringok, partial, atau unavailable.
metadata_issuesobjectCatatan masalah per bidang dengan kunci name, symbol, decimals, total_supply, dan nilai reverted, no_data, invalid_encoding, atau temporarily_unavailable.

{token} yang bukan alamat 20-byte yang valid mengembalikan 400 bad_request; {token} yang tidak dikenal mengembalikan 404 not_found; {chain} yang tidak dikenal mengembalikan 404 unknown_chain.

Endpoint saldo sudah menyertakan symbol dan decimals jika tersedia, tetapi keduanya dapat bernilai null. Untuk melengkapi nama dan decimals setiap token dalam dompet, gunakan POST /{chain}/tokens:batch:

  • Body permintaan adalah {"addresses": [...]} dengan paling banyak 100 alamat per permintaan; lebih dari 100 entri, atau entri yang bukan alamat 20-byte yang valid, mengembalikan 400 bad_request (gagal pada alamat tidak valid pertama yang ditemui).
  • Alamat yang tidak ditemukan tidak memicu error; alamat tersebut tercantum dalam data.missing, sedangkan data.tokens hanya berisi token yang metadatanya ditemukan.
  • Alamat berulang dideduplikasi dalam tokens dan missing, masing-masing mengikuti urutan kemunculan pertama dalam permintaan.
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"]}'

Menskalakan jumlah berdasarkan decimals

Bidang saldo balance dan bidang transfer ERC-20 amount adalah bilangan bulat mentah yang ditampilkan sebagai string desimal (UInt256String); total_supply token juga merupakan bilangan bulat on-chain mentah tanpa penskalaan decimals. Untuk menampilkan jumlah yang mudah dibaca manusia, bagi berdasarkan decimals token tersebut.

  • decimals berasal dari symbol/decimals item saldo itu sendiri, atau dari GET /{chain}/tokens/{token} dan POST /{chain}/tokens:batch; nilainya dapat berupa null.
  • Nilai ini dapat melebihi 2^53, jadi jangan menghitung menggunakan angka JSON: gunakan BigInt dalam TypeScript dan Decimal dalam Python, dengan mem-parsing string desimal apa adanya untuk menghindari hilangnya presisi.
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);

Kebaruan data

Setiap respons sukses dengan cakupan chain menyertakan meta:

  • as_of_block: blok terbaru chain yang sepenuhnya sudah ditulis. Endpoint dengan cakupan blok menyajikan data hingga tinggi ini.
  • safe_block: penanda tag blok konsensus safe node (null jika belum diketahui). Tidak pernah di bawah finalized_block, dan tidak memangkas, menolak, atau menunda respons.
  • finalized_block: penanda tag blok konsensus finalized node (null jika belum diketahui). Tidak memangkas, menolak, atau menunda respons; klien menentukan keamanan yang dibutuhkan dari penanda tersebut (seperti status konfirmasi).
  • coverage: "full" atau "partial". Transfer alamat dan endpoint serupa melaporkan "partial" ketika clamp mempersempit jendela yang disajikan, atau ketika jendela dimulai sebelum blok pertama yang diindeks pada chain.
  • refreshed_at: waktu data di balik respons terakhir diperbarui (UTC). Dapat bernilai null: null berarti waktu pembaruan data tidak diketahui dan data harus dianggap usang; endpoint berbasis blok selalu mengembalikan nilai.
  • Respons juga mengulang chain, chain_slug, dan chain_external_id.

Pola yang umum: baca meta.as_of_block dari respons pertama mana pun untuk membaca hingga blok terbaru yang diindeks, dan periksa meta.safe_block / meta.finalized_block jika ingin menampilkan status terkonfirmasi.

Estimasi CU untuk satu pemuatan halaman

Setiap metode ditagih berdasarkan bobot CU-nya, yang dibaca dari API paket platform:

Bobot CU per panggilan

MetodeCU per panggilan
data.address_balances25
data.address_transfers25
data.tokens_batch10

Satu kali muat halaman (perkiraan)

1 permintaan saldo + 3 halaman transfer + 1 permintaan tokens:batch, total 5 panggilan, sekitar 110 CU. Penggunaan sebenarnya tergantung pada jumlah halaman dan token.

Untuk keputusan penagihan dan respons error yang tidak ditagih, lihat aturan penagihan. Jika yang Anda butuhkan bukan riwayat transfer terindeks melainkan log dari blok terbaru, baca Data node terbaru vs riwayat terindeks sebelum memutuskan untuk beralih ke eth_getLogs.

Langkah selanjutnya

Terakhir diperbarui:

Di halaman ini