Wallet-Token-Guthaben-API: ERC-20-Assets und Transferverlauf

Erstellen Sie eine Wallet-Assets-Seite mit ERC-20-Token-Guthaben ungleich null, Token-Transferverlauf und Batch-Metadaten. Prüfen Sie die Chain-Abdeckung, paginieren Sie Ergebnisse und skalieren Sie ganzzahlige Beträge anhand der Dezimalstellen.

Erstellen Sie eine Wallet-Assets-Seite mit der Blockchain-Wallet-Data-API: Verwenden Sie die Token Balances API für ERC-20-Bestände ungleich null und die Token Transfers API für den Wallet-Verlauf. Entwickler und KI-Agenten nutzen dieselben authentifizierten Anfragen. Lesen Sie vor der Abfrage GET /v1/status und prüfen Sie data_features sowie data_status der ausgewählten Chain; die Guthabenabdeckung variiert je nach Chain. Anfrageparameter und Antwortschemas finden Sie in der Data-API-Referenz.

Aufgaben, die dieser Leitfaden abdeckt

Die drei Arten von Daten, die eine Wallet-Assets-Seite benötigt

Eine Wallet-Assets-Seite kann die ERC-20-Token-Guthaben, den Token-Transferverlauf und die Token-Metadaten einer Adresse anzeigen. Die Data API stellt für jeden Bereich einen Endpunkt bereit:

  • Guthaben: GET /{chain}/addresses/{address}/balances gibt die ERC-20-Guthaben der Adresse ungleich null zurück, aufsteigend sortiert nach der token-Adresse, wobei Token-symbol und decimals enthalten sind, sofern verfügbar. Eine Adresse ohne Guthaben gibt 200 mit data: [] zurück.
  • Transfers: GET /{chain}/addresses/{address}/transfers gibt Token-Transfers zurück, an denen die Adresse innerhalb eines erforderlichen Blockfensters beteiligt ist, absteigend sortiert nach (block_number, log_index).
  • Token-Metadaten: GET /{chain}/tokens/{token} liest Name, Symbol, Dezimalstellen und Gesamtangebot eines einzelnen Tokens anhand der Contract-Adresse aus; POST /{chain}/tokens:batch liest dieselben Metadaten für bis zu 100 Adressen in einer einzigen Anfrage aus.

Alle drei verwenden https://api.blockvectra.com/v1/data als Basis-URL und den x-api-key-Anfrage-Header, mit robinhood_mainnet als Beispiel-Chain. Sie gehören zu den Fähigkeiten balances, transfers bzw. token_metadata; welche Chains die jeweilige Fähigkeit bieten, sehen Sie auf der Seite Unterstützte Chains. Auf einer Chain ohne die entsprechende Fähigkeit gibt der Endpunkt 422 no_coverage zurück.

Anfrage 1: Adress-Guthaben

Dieser Endpunkt erfordert weniger Parameter und eignet sich daher gut als erste Anfrage für eine Seite:

  • {chain} (Pfadparameter, erforderlich): Chain-Kennung, der chain-Wert eines Eintrags in GET /chains (zum Beispiel robinhood_mainnet). Der Abgleich erfolgt exakt und case-sensitiv; Aliase und numerische Chain-IDs werden nicht akzeptiert.
  • {address} (Pfadparameter, erforderlich): 20-Byte-Adresse; das Präfix 0x ist optional und Groß-/Kleinschreibung wird gleichermaßen akzeptiert.
  • limit (Query-Parameter, optional): Seitengröße. Standardmäßig 50; Werte über 500 werden auf 500 begrenzt; 0 oder ein Nicht-Ganzzahl-Wert gibt 400 bad_request zurück.
  • cursor (Query-Parameter, optional): Der next_cursor der vorherigen Antwort, der unverändert übergeben wird, um die nächste Seite abzurufen. Ein Cursor ist nur für die Chain, den Endpunkt und die Query-Parameter gültig, die ihn ausgegeben haben; eine Wiederverwendung an anderer Stelle gibt 400 bad_request zurück.

Er ist keyset-paginiert: next_cursor erscheint nur, wenn eine weitere Seite vorhanden ist. Auf der letzten Seite fehlt der Schlüssel vollständig, niemals 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"

Der Antwort-Envelope ist AddressBalanceListEnvelope, der data und meta enthält. Jedes Element von data ist ein AddressBalance:

FeldTypBeschreibung
tokenstring (Adresse)Token-Contract-Adresse; die kanonische Form ist 0x plus 40 hexadezimale Kleinbuchstaben.
balancestring (Dezimal)Rohes ganzzahliges Guthaben, das 2^53 überschreiten kann, zurückgegeben als einfache Dezimalzeichenfolge — niemals eine JSON-Zahl, wissenschaftliche Notation oder Hex.
symbolstring oder nullToken-Symbol oder null, falls nicht verfügbar.
decimalsinteger oder nullToken-Dezimalstellen, 0–255, oder null, falls nicht verfügbar.

Anfrage 2: Adress-Transfers

Der Transfers-Endpunkt erfordert ein explizites Blockfenster: from_block und to_block sind beide erforderlich und müssen from_block <= to_block erfüllen. Er akzeptiert einige weitere Parameter:

  • standard (Query-Parameter, erforderlich): erc20 oder erc721. Adressbezogene Abfragen decken erc1155 nicht ab; die Übergabe gibt 422 no_coverage zurück.
  • direction (Query-Parameter, optional): in, out oder any; standardmäßig any und filtert nach Richtung relativ zur Adresse.
  • token (Query-Parameter, optional): Beschränkt die Ergebnisse auf einen einzelnen Token-Contract.
  • clamp (Query-Parameter, optional): Nur die Literal-Zeichenfolge true aktiviert dies; jeder andere Wert wird als false behandelt.

Fenstergrenzen und Finalität: Ein explizites to_block über as_of_block gibt 409 not_indexed_yet zurück, es sei denn, clamp=true kürzt es auf as_of_block herunter; ein Fenster, das breiter als das Limit der Chain ist (limits.max_window_blocks aus GET /chains), gibt 409 window_too_large zurück, es sei denn, clamp=true schneidet am älteren Ende ab (erhöht from_block und hält to_block fest). Wenn from_block selbst bereits hinter as_of_block liegt, bleibt es auch mit clamp=true ein hartes 409. Wenn das Fenster gekürzt oder nur teilweise abgedeckt ist, lautet meta.coverage in der Antwort "partial"; andernfalls "full".

In den Transfer-Datensätzen enthalten ERC-20-Elemente zusätzlich amount; ERC-721-Elemente enthalten token_id. Beide enthalten token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index und 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"

Durch alle Transfers paginieren

Der next_cursor des Adress-Transfers-Endpunkts ist optimistisch: Er erscheint nur, wenn die Seite genau limit Zeilen zurückgegeben hat, sodass eine Seite einen next_cursor tragen kann und sich dennoch als die letzte Seite herausstellt. Halten Sie nicht an, wenn eine Seite leer ist; folgen Sie next_cursor, bis der Schlüssel fehlt.

Der folgende Code ruft jeden Transfer im Fenster ab:

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

Anfrage 3: Token-Metadaten und tokens:batch

Lesen Sie einen einzelnen Token mit GET /{chain}/tokens/{token} aus; der Pfad erfordert lediglich {chain} und {token}, ohne Paginierung. Der Antwort-Envelope ist TokenEnvelope, und data ist ein Token:

FeldTypBeschreibung
addressstring (Adresse)Token-Contract-Adresse.
standardstringerc20, erc721 oder unknown.
namestring oder nullToken-Name oder null, falls nicht verfügbar.
symbolstring oder nullToken-Symbol oder null, falls nicht verfügbar.
decimalsinteger oder nullToken-Dezimalstellen, 0–255, oder null, falls nicht verfügbar.
total_supplystring oder nullRohes Gesamtangebot; die API wendet keine decimals-Skalierung an. null, falls nicht verfügbar.
first_seen_blockinteger (int64)Blockhöhe, bei der der Token erstmals gesehen wurde.
metadata_updated_atstring (Zeitstempel)UTC-Zeitpunkt, zu dem die Metadaten zuletzt aktualisiert wurden.
metadata_blockinteger (int64)Blockhöhe, bei der die Metadaten ausgelesen wurden.
metadata_statusstringok, partial oder unavailable.
metadata_issuesobjectProblemaufzeichnungen pro Feld mit den Schlüsseln name, symbol, decimals, total_supply und den Werten reverted, no_data, invalid_encoding oder temporarily_unavailable.

Ein {token}, der keine gültige 20-Byte-Adresse ist, gibt 400 bad_request zurück; ein unbekannter {token} gibt 404 not_found zurück; eine unbekannte {chain} gibt 404 unknown_chain zurück.

Der Guthaben-Endpunkt enthält bereits symbol und decimals, sofern verfügbar, jedoch können beide null sein. Um Name und Dezimalstellen für jeden Token in einer Wallet zu ergänzen, verwenden Sie POST /{chain}/tokens:batch:

  • Der Request-Body ist {"addresses": [...]} mit höchstens 100 Adressen pro Anfrage; mehr als 100 Einträge oder ein Eintrag, der keine gültige 20-Byte-Adresse ist, gibt 400 bad_request zurück (die Anfrage schlägt bei der ersten ungültigen Adresse fehl, auf die sie trifft).
  • Adressen, die nicht gefunden werden, lösen keinen Fehler aus; sie werden in data.missing aufgeführt, während data.tokens nur diejenigen Token enthält, deren Metadaten gefunden wurden.
  • Doppelte Adressen werden sowohl in tokens als auch in missing dedupliziert, jeweils in der Reihenfolge des ersten Auftretens in der Anfrage.
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"]}'

Beträge anhand von Dezimalstellen skalieren

Das Guthabenfeld balance und das ERC-20-Transferfeld amount sind rohe Ganzzahlen, die als Dezimalzeichenfolgen dargestellt werden (UInt256String); auch das total_supply eines Tokens ist eine rohe On-Chain-Ganzzahl ohne angewandte decimals-Skalierung. Um eine lesbare Menge anzuzeigen, teilen Sie durch die decimals dieses Tokens.

  • decimals stammt aus symbol/decimals des Guthabenelements selbst oder aus GET /{chain}/tokens/{token} und POST /{chain}/tokens:batch; es kann null sein.
  • Diese Werte können 2^53 überschreiten; führen Sie die Berechnung daher nicht mit einer JSON-Zahl durch: Verwenden Sie BigInt in TypeScript und Decimal in Python und parsen Sie die Dezimalzeichenfolge unverändert, um Präzisionsverluste zu vermeiden.
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);

Datenaktualität

Jede chainbezogene Erfolgsantwort enthält meta:

  • as_of_block: Der neueste vollständig geschriebene Block der Chain. Blockbezogene Endpunkte liefern Daten bis zu dieser Höhe.
  • safe_block: Eine Markierung, die den Konsens-Block-Tag safe des Nodes angibt (null, solange unbekannt). Liegt niemals unter finalized_block und führt weder zum Abschneiden, Abweisen noch zum Verzögern von Antworten.
  • finalized_block: Eine Markierung, die den Konsens-Block-Tag finalized des Nodes angibt (null, solange unbekannt). Führt weder zum Abschneiden, Abweisen noch zum Verzögern von Antworten; Clients entscheiden selbst, welche Sicherheit sie anhand der Markierung benötigen (beispielsweise einen Bestätigungsstatus).
  • coverage: "full" oder "partial". Adress-Transfers und ähnliche Endpunkte melden "partial", wenn clamp das bereitgestellte Fenster eingeengt hat oder wenn das Fenster vor dem ersten indexierten Block der Chain beginnt.
  • refreshed_at: Zeitpunkt, zu dem die Daten hinter der Antwort zuletzt aktualisiert wurden (UTC). Kann null sein: null bedeutet, dass die Aktualisierungszeit der Daten unbekannt ist und sie als veraltet behandelt werden sollten; blockbasierte Endpunkte geben stets einen Wert zurück.
  • Es wiederholt außerdem chain, chain_slug und chain_external_id.

Ein gängiges Muster: Lesen Sie meta.as_of_block aus einer beliebigen ersten Antwort aus, um bis zum neuesten indexierten Block zu lesen, und prüfen Sie meta.safe_block / meta.finalized_block, falls Sie einen bestätigten Status anzeigen möchten.

CU-Schätzung für einen Seitenaufruf

Jede Methode wird nach ihrem CU-Gewicht abgerechnet, das aus der Plans-API der Plattform ausgelesen wird:

CU-Gewichtung pro Aufruf

MethodeCU pro Aufruf
data.address_balances25
data.address_transfers25
data.tokens_batch10

Ein Seitenaufruf (geschätzt)

1 balances-Anfrage + 3 Transfer-Seiten + 1 tokens:batch-Anfrage(n), 5 Aufrufe insgesamt, ca. 110 CU. Der tatsächliche Verbrauch hängt von der Anzahl der Seiten und Token ab.

Abrechnungsentscheidungen und nicht abgerechnete Fehlerantworten finden Sie unter Abrechnungsregeln. Wenn Sie statt des indexierten Transferverlaufs Logs aus den neuesten Blöcken benötigen, lesen Sie zuerst Aktuelle Node-Daten vs. indexierter Verlauf, bevor Sie entscheiden, ob Sie zu eth_getLogs wechseln.

Nächste Schritte

Zuletzt aktualisiert:

Auf dieser Seite