API de saldos de tokens de billeteras: activos ERC-20 e historial de transferencias
Cree una página de activos de billetera con saldos de tokens ERC-20 distintos de cero, historial de transferencias y metadatos por lotes. Compruebe la cobertura por cadena, pagine resultados y ajuste importes enteros según los decimales.
Cree una página de activos de billetera con la API de datos de billeteras blockchain: utilice la API de saldos de tokens para las tenencias ERC-20 distintas de cero y la API de transferencias de tokens para el historial de la billetera. Los desarrolladores y agentes de IA utilizan las mismas solicitudes autenticadas. Antes de consultar, lea GET /v1/status y compruebe data_features y data_status de la cadena seleccionada; la cobertura de saldos varía según la cadena. Los parámetros de solicitud y los esquemas de respuesta están en la referencia de la Data API.
Tareas que esta guía le ayuda a completar
- Leer los saldos de tokens de una billetera con una clave y paginar las tenencias ERC-20 distintas de cero.
- Leer el historial de transferencias de una billetera dentro de una ventana de bloques fija y seguir los cursores para la dirección seleccionada.
- Completar los metadatos de tokens para mostrar nombres y símbolos junto a los saldos enteros brutos, conservando los campos faltantes.
Los tres tipos de datos que necesita una página de activos de billetera
Una página de activos de billetera puede mostrar los saldos de tokens ERC-20 de una dirección, su historial de transferencias y los metadatos de tokens. La Data API ofrece un endpoint para cada uno:
- Saldos:
GET /{chain}/addresses/{address}/balancesdevuelve los saldos ERC-20 distintos de cero de la dirección, ordenados por direccióntokende forma ascendente, consymbolydecimalsdel token cuando están disponibles. Una dirección sin saldos devuelve200condata: []. - Transferencias:
GET /{chain}/addresses/{address}/transfersdevuelve las transferencias de tokens que involucran a la dirección dentro de una ventana de bloques obligatoria, ordenadas por(block_number, log_index)de forma descendente. - Metadatos de tokens:
GET /{chain}/tokens/{token}lee el nombre, símbolo, decimales y suministro total de un token por dirección de contrato;POST /{chain}/tokens:batchlee los mismos metadatos de hasta 100 direcciones en una solicitud.
Los tres utilizan https://api.blockvectra.com/v1/data como URL base y el encabezado de solicitud x-api-key, con robinhood_mainnet como cadena de ejemplo. Pertenecen a las capacidades balances, transfers y token_metadata, respectivamente; para conocer las cadenas que ofrecen cada capacidad, consulte la página de Cadenas compatibles. En una cadena sin esa capacidad, el endpoint devuelve 422 no_coverage.
Solicitud 1: saldos de una dirección
Este endpoint requiere menos parámetros, por lo que resulta adecuado como primera solicitud de una página:
{chain}(parámetro de ruta, obligatorio): identificador de la cadena, el valorchainde una entrada deGET /chains(por ejemplorobinhood_mainnet). La coincidencia es exacta y distingue mayúsculas y minúsculas; no se aceptan alias ni IDs de cadena numéricos.{address}(parámetro de ruta, obligatorio): dirección de 20 bytes; el prefijo0xes opcional y se aceptan mayúsculas y minúsculas.limit(parámetro de consulta, opcional): tamaño de página. El valor predeterminado es 50; los valores superiores a 500 se limitan a 500;0o un valor no entero devuelve400 bad_request.cursor(parámetro de consulta, opcional): elnext_cursorde la respuesta anterior, devuelto sin cambios para obtener la siguiente página. Un cursor solo es válido para la cadena, el endpoint y los parámetros de consulta que lo generaron; reutilizarlo en otro contexto devuelve400 bad_request.
La paginación utiliza claves: next_cursor aparece solo cuando hay otra página. En la última página, la clave está completamente ausente, nunca es 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"La estructura de respuesta es AddressBalanceListEnvelope, que contiene data y meta. Cada elemento de data es un AddressBalance:
| Campo | Tipo | Descripción |
|---|---|---|
token | string (dirección) | Dirección del contrato del token; la forma canónica es 0x seguido de 40 dígitos hexadecimales en minúsculas. |
balance | string (decimal) | Saldo entero bruto, que puede superar 2^53, devuelto como una cadena decimal simple; nunca como un número JSON, notación científica ni hexadecimal. |
symbol | string o null | Símbolo del token, o null si no está disponible. |
decimals | integer o null | Decimales del token, 0–255, o null si no están disponibles. |
Solicitud 2: transferencias de una dirección
El endpoint de transferencias requiere una ventana de bloques explícita: tanto from_block como to_block son obligatorios y deben cumplir from_block <= to_block. Acepta algunos parámetros adicionales:
standard(parámetro de consulta, obligatorio):erc20oerc721. Las consultas por dirección no cubrenerc1155; pasarlo devuelve422 no_coverage.direction(parámetro de consulta, opcional):in,outoany; el valor predeterminado esanyy filtra por dirección de la transferencia respecto de la dirección consultada.token(parámetro de consulta, opcional): restringe los resultados a un contrato de token.clamp(parámetro de consulta, opcional): solo la cadena literaltruelo activa; cualquier otro valor se trata comofalse.
Límites de ventana y finalidad: un to_block explícito superior a as_of_block devuelve 409 not_indexed_yet, salvo que clamp=true lo reduzca a as_of_block; una ventana más amplia que el límite de la cadena (limits.max_window_blocks de GET /chains) devuelve 409 window_too_large, salvo que clamp=true recorte el extremo más antiguo (aumentando from_block y manteniendo fijo to_block). Si el propio from_block ya supera as_of_block, sigue devolviendo un 409 obligatorio incluso con clamp=true. Cuando la ventana se recorta o solo tiene cobertura parcial, meta.coverage de la respuesta es "partial"; en caso contrario, es "full".
En los registros de transferencias, los elementos ERC-20 añaden amount; los elementos ERC-721 añaden token_id. Ambos incluyen token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index y 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"Paginar todas las transferencias
El next_cursor del endpoint de transferencias por dirección es optimista: aparece solo cuando la página devuelve exactamente limit filas, por lo que una página puede incluir un next_cursor y aun así resultar ser la última. No se detenga cuando una página esté vacía; siga next_cursor hasta que la clave esté ausente.
El código siguiente obtiene todas las transferencias de la ventana:
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);Solicitud 3: metadatos de tokens y tokens:batch
Lea un token individual con GET /{chain}/tokens/{token}; la ruta acepta solo {chain} y {token}, sin paginación. La estructura de respuesta es TokenEnvelope y data es un Token:
| Campo | Tipo | Descripción |
|---|---|---|
address | string (dirección) | Dirección del contrato del token. |
standard | string | erc20, erc721 o unknown. |
name | string o null | Nombre del token, o null si no está disponible. |
symbol | string o null | Símbolo del token, o null si no está disponible. |
decimals | integer o null | Decimales del token, 0–255, o null si no están disponibles. |
total_supply | string o null | Suministro total bruto; la API no aplica el ajuste por decimals. Es null si no está disponible. |
first_seen_block | integer (int64) | Altura del bloque en el que se detectó el token por primera vez. |
metadata_updated_at | string (marca de tiempo) | Hora UTC de la última actualización de los metadatos. |
metadata_block | integer (int64) | Altura del bloque en el que se leyeron los metadatos. |
metadata_status | string | ok, partial o unavailable. |
metadata_issues | object | Registros de problemas por campo con claves name, symbol, decimals, total_supply y valores reverted, no_data, invalid_encoding o temporarily_unavailable. |
Un {token} que no sea una dirección válida de 20 bytes devuelve 400 bad_request; un {token} desconocido devuelve 404 not_found; un {chain} desconocido devuelve 404 unknown_chain.
El endpoint de saldos ya incluye symbol y decimals cuando están disponibles, pero ambos pueden ser null. Para completar el nombre y los decimales de cada token de una billetera, utilice POST /{chain}/tokens:batch:
- El body de la solicitud es
{"addresses": [...]}con un máximo de 100 direcciones por solicitud; más de 100 entradas o una entrada que no sea una dirección válida de 20 bytes devuelve400 bad_request(falla en la primera dirección inválida que encuentra al recorrerlas). - Las direcciones no encontradas no generan un error; se enumeran en
data.missing, mientras quedata.tokenscontiene solo los tokens cuyos metadatos se encontraron. - Las direcciones duplicadas se deduplican tanto en
tokenscomo enmissing, cada uno siguiendo el orden de la primera aparición en la solicitud.
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"]}'Ajustar los importes según los decimales
El campo de saldo balance y el campo de transferencia ERC-20 amount son enteros brutos representados como cadenas decimales (UInt256String); el total_supply de un token también es un entero bruto on-chain sin ajuste por decimals. Para mostrar una cantidad legible por humanos, divida según los decimals de ese token.
decimalsprocede de los propiossymbol/decimalsdel elemento de saldo o deGET /{chain}/tokens/{token}yPOST /{chain}/tokens:batch; puede sernull.- Estos valores pueden superar
2^53, así que no realice los cálculos con un número JSON: utiliceBigInten TypeScript yDecimalen Python, analizando la cadena decimal tal como está para evitar pérdidas de precisión.
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);Actualidad de los datos
Cada respuesta exitosa de una cadena incluye meta:
as_of_block: el bloque más reciente de la cadena completamente escrito. Los endpoints por bloques sirven datos hasta esta altura.safe_block: un marcador que indica la etiqueta de bloque de consensosafedel nodo (nullmientras se desconoce). Nunca es inferior afinalized_blocky no recorta, rechaza ni retrasa respuestas.finalized_block: un marcador que indica la etiqueta de bloque de consensofinalizeddel nodo (nullmientras se desconoce). No recorta, rechaza ni retrasa respuestas; los clientes deciden qué seguridad necesitan del marcador (como el estado de confirmación).coverage:"full"o"partial". Las transferencias por dirección y endpoints similares informan"partial"cuandoclampredujo la ventana servida o cuando la ventana comienza antes del primer bloque indexado de la cadena.refreshed_at: cuándo se actualizaron por última vez los datos de la respuesta (UTC). Puede sernull:nullsignifica que se desconoce la hora de actualización de los datos y deben tratarse como desactualizados; los endpoints basados en bloques siempre devuelven un valor.- También repite
chain,chain_slugychain_external_id.
Un patrón habitual: lea meta.as_of_block de cualquier primera respuesta para consultar hasta el bloque indexado más reciente y compruebe meta.safe_block / meta.finalized_block si desea mostrar el estado confirmado.
Estimación de CU para una carga de página
Cada método se factura según su peso CU, leído de la API de planes de la plataforma:
Peso en CU por llamada
| Método | CU por llamada |
|---|---|
data.address_balances | 25 |
data.address_transfers | 25 |
data.tokens_batch | 10 |
Una carga de página (estimada)
1 solicitud de saldos + 3 páginas de transferencias + 1 solicitud(es) tokens:batch, 5 llamadas en total, aprox. 110 CU. El uso real depende del número de páginas y tokens.
Para conocer los criterios de facturación y las respuestas de error no facturadas, consulte las reglas de facturación. Si necesita logs de los bloques más recientes en lugar de historial de transferencias indexadas, lea primero Datos recientes del nodo frente a historial indexado antes de decidir si cambia a eth_getLogs.
Próximos pasos
- Explore el directorio de conjuntos de datos para ver todos los conjuntos de datos indexados por BlockVectra.
- Consulte el plan gratuito y los precios para comprobar lo que incluye su cuenta.
- Inicie sesión en la consola para crear una API key.
Última actualización:
Acciones tokenizadas
Consulte tablas de clasificación diaria y métricas históricas de acciones tokenizadas con la Data API, cubriendo campos, convenciones de codificación, paginación y estimaciones de uso.
RPC personalizado de billetera
Añada una URL RPC de BlockVectra a MetaMask o Rabby. Consulte los IDs de cadena y símbolos nativos, configure una API key en la ruta y gestione una clave dedicada para la billetera.