API балансов токенов кошелька: активы ERC-20 и история переводов
Создайте страницу активов кошелька с ненулевыми балансами токенов ERC-20, историей переводов токенов и пакетными метаданными. Проверяйте покрытие сетей, выполняйте пагинацию и масштабируйте целочисленные суммы по decimals.
Создайте страницу активов кошелька с помощью API данных блокчейн-кошельков: используйте API балансов токенов для ненулевых остатков ERC-20 и API переводов токенов для истории кошелька. Разработчики и AI Agent используют одинаковые аутентифицированные запросы. Перед отправкой запросов прочитайте GET /v1/status и проверьте data_features и data_status выбранной сети; покрытие балансов различается в зависимости от сети. Параметры запросов и схемы ответов приведены в справочнике Data API.
Задачи, которые помогает решить это руководство
- Чтение балансов токенов кошелька с использованием ключа и пагинация ненулевых остатков ERC-20.
- Чтение истории переводов кошелька в пределах фиксированного окна блоков с переходом по курсорам для выбранного адреса.
- Заполнение метаданных токенов для отображения названий и символов рядом с исходными целочисленными балансами с сохранением отсутствующих полей.
Три типа данных, необходимых странице активов кошелька
Страница активов кошелька может отображать балансы токенов ERC-20 адреса, историю переводов токенов и метаданные токенов. Data API предоставляет эндпоинт для каждого из них:
- Балансы:
GET /{chain}/addresses/{address}/balancesвозвращает ненулевые балансы ERC-20 адреса, отсортированные по адресуtokenпо возрастанию, с включениемsymbolиdecimalsтокена, если они доступны. Адрес без балансов возвращает200сdata: []. - Переводы:
GET /{chain}/addresses/{address}/transfersвозвращает переводы токенов с участием адреса в пределах обязательного окна блоков, отсортированные по(block_number, log_index)по убыванию. - Метаданные токенов:
GET /{chain}/tokens/{token}считывает название, символ, decimals и общее предложение одного токена по адресу контракта;POST /{chain}/tokens:batchсчитывает те же метаданные для количества адресов до 100 в одном запросе.
Все три эндпоинта используют https://api.blockvectra.com/v1/data в качестве базового URL и заголовок запроса x-api-key, при этом robinhood_mainnet используется в качестве примера сети. Они относятся к возможностям balances, transfers и token_metadata соответственно; список сетей, поддерживающих каждую возможность, см. на странице Поддерживаемые сети. В сети без соответствующей возможности эндпоинт возвращает 422 no_coverage.
Запрос 1: балансы адреса
Этот эндпоинт принимает меньше параметров, что делает его удобным первым запросом для страницы:
{chain}(параметр пути, обязательный): идентификатор сети, значениеchainэлемента вGET /chains(например,robinhood_mainnet). Сопоставление точное и чувствительное к регистру; псевдонимы и числовые Chain ID не принимаются.{address}(параметр пути, обязательный): 20-байтовый адрес; префикс0xнеобязателен, допускается любой регистр.limit(параметр запроса, необязательный): размер страницы. По умолчанию 50; значения выше 500 ограничиваются до 500;0или нецелое число возвращает400 bad_request.cursor(параметр запроса, необязательный):next_cursorиз предыдущего ответа, передаваемый без изменений для получения следующей страницы. Курсор действителен только для сети, эндпоинта и параметров запроса, которые его выдали; его повторное использование в другом месте возвращает400 bad_request.
Здесь используется keyset-пагинация: next_cursor появляется только при наличии следующей страницы. На последней странице ключ полностью отсутствует, никогда не принимая значение 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"Конвертом ответа является AddressBalanceListEnvelope, содержащий data и meta. Каждый элемент в data представляет собой AddressBalance:
| Поле | Тип | Описание |
|---|---|---|
token | string (address) | Адрес контракта токена; каноническая форма — 0x плюс 40 шестнадцатеричных символов в нижнем регистре. |
balance | string (decimal) | Исходный целочисленный баланс, который может превышать 2^53, возвращаемый в виде обычной десятичной строки — никогда не число JSON, экспоненциальная запись или hex. |
symbol | string or null | Символ токена или null, если он недоступен. |
decimals | integer or null | Количество десятичных знаков токена, от 0 до 255, или null, если оно недоступно. |
Запрос 2: переводы адреса
Эндпоинт переводов требует явного окна блоков: from_block и to_block оба обязательны и должны удовлетворять условию from_block <= to_block. Он принимает еще несколько параметров:
standard(параметр запроса, обязательный):erc20илиerc721. Запросы с областью видимости адреса не охватываютerc1155; его передача возвращает422 no_coverage.direction(параметр запроса, необязательный):in,outилиany; по умолчаниюany, фильтрует по направлению относительно адреса.token(параметр запроса, необязательный): ограничивает результаты одним контрактом токена.clamp(параметр запроса, необязательный): только литеральная строкаtrueвключает этот параметр; любое другое значение обрабатывается какfalse.
Границы окна и финальность: явное указание to_block выше as_of_block возвращает 409 not_indexed_yet, если только clamp=true не усечет его до as_of_block; окно шире лимита сети (limits.max_window_blocks из GET /chains) возвращает 409 window_too_large, если только clamp=true не усечет его со стороны более старых блоков (увеличивая from_block и сохраняя фиксированным to_block). Если сам from_block уже находится дальше as_of_block, запрос возвращает строгую ошибку 409 даже при clamp=true. Когда окно усечено или частично покрыто, meta.coverage в ответе принимает значение "partial"; в противном случае — "full".
В записях о переводах элементы ERC-20 содержат поле amount; элементы ERC-721 содержат поле token_id. Оба включают token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index и log_index.
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# 1) Чтение as_of_block из ответа балансов.
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 усекает слишком широкое окно или to_block выше as_of_block вместо возврата 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"Пагинация по всем переводам
Параметр next_cursor эндпоинта переводов адреса оптимистичен: он появляется только тогда, когда страница вернула ровно limit строк, поэтому страница может содержать next_cursor и все равно оказаться последней страницей. Не останавливайтесь, когда страница пуста; следуйте по next_cursor, пока этот ключ не будет отсутствовать.
Приведенный ниже код получает все переводы в пределах окна:
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 усекает окно со стороны более старых блоков
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; // отсутствует на последней странице
} while (cursor);Запрос 3: метаданные токенов и tokens:batch
Получите данные одного токена с помощью GET /{chain}/tokens/{token}; путь принимает только {chain} и {token}, без пагинации. Конвертом ответа является TokenEnvelope, а data представляет собой Token:
| Поле | Тип | Описание |
|---|---|---|
address | string (address) | Адрес контракта токена. |
standard | string | erc20, erc721 или unknown. |
name | string or null | Название токена или null, если оно недоступно. |
symbol | string or null | Символ токена или null, если он недоступен. |
decimals | integer or null | Количество десятичных знаков токена, от 0 до 255, или null, если оно недоступно. |
total_supply | string or null | Исходное общее предложение; API не применяет масштабирование по decimals. null, если недоступно. |
first_seen_block | integer (int64) | Высота блока, в котором токен был впервые обнаружен. |
metadata_updated_at | string (timestamp) | Время UTC, когда метаданные были обновлены в последний раз. |
metadata_block | integer (int64) | Высота блока, на котором были прочитаны метаданные. |
metadata_status | string | ok, partial или unavailable. |
metadata_issues | object | Записи о проблемах для отдельных полей с ключами name, symbol, decimals, total_supply и значениями reverted, no_data, invalid_encoding или temporarily_unavailable. |
Значение {token}, не являющееся корректным 20-байтовым адресом, возвращает 400 bad_request; неизвестный {token} возвращает 404 not_found; неизвестный {chain} возвращает 404 unknown_chain.
Эндпоинт балансов уже включает symbol и decimals, если они доступны, но оба значения могут быть null. Чтобы заполнить название и decimals для каждого токена в кошельке, используйте POST /{chain}/tokens:batch:
- Тело запроса представляет собой
{"addresses": [...]}с не более чем 100 адресами на запрос; более 100 записей или запись, не являющаяся корректным 20-байтовым адресом, возвращает400 bad_request(запрос завершается ошибкой на первом встретившемся некорректном адресе). - Ненайденные адреса не вызывают ошибку; они перечисляются в
data.missing, тогда какdata.tokensсодержит только токены, метаданные которых были найдены. - Повторяющиеся адреса дедуплицируются как в
tokens, так и вmissing, в порядке их первого появления в запросе.
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"]}'Масштабирование сумм с учетом decimals
Поле баланса balance и поле перевода ERC-20 amount представляют собой исходные целые числа, представленные в виде десятичных строк (UInt256String); значение total_supply токена также является исходным ончейн-целым числом без применения масштабирования по decimals. Чтобы отобразить понятное человеку количество, разделите значение на decimals этого токена.
- Значение
decimalsберется из полейsymbol/decimalsсамого элемента баланса либо изGET /{chain}/tokens/{token}иPOST /{chain}/tokens:batch; оно может бытьnull. - Эти значения могут превышать
2^53, поэтому не выполняйте арифметические вычисления с числами JSON: используйтеBigIntв TypeScript иDecimalв Python, выполняя парсинг десятичной строки как есть во избежание потери точности.
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);Свежесть данных
Каждый успешный ответ в рамках сети содержит объект meta:
as_of_block: новейший полностью записанный блок сети. Эндпоинты в диапазоне блоков отдают данные вплоть до этой высоты.safe_block: маркер, указывающий тег консенсуса нодыsafe(null, пока неизвестен). Никогда не бывает нижеfinalized_blockи не усекает, не отклоняет и не задерживает ответы.finalized_block: маркер, указывающий тег консенсуса нодыfinalized(null, пока неизвестен). Он не усекает, не отклоняет и не задерживает ответы; клиенты сами определяют требуемый уровень надежности на основе маркера (например, статус подтверждения).coverage:"full"или"partial". Эндпоинты переводов адреса и аналогичные сообщают"partial", когдаclampсузил отдаваемое окно или когда окно начинается раньше первого проиндексированного блока сети.refreshed_at: время последнего обновления данных, лежащих в основе ответа (UTC). Может бытьnull:nullозначает, что время обновления данных неизвестно и их следует считать устаревшими; блочные эндпоинты всегда возвращают значение.- Он также повторяет
chain,chain_slugиchain_external_id.
Распространенный шаблон: прочитайте meta.as_of_block из любого первого ответа, чтобы получить данные вплоть до новейшего проиндексированного блока, и проверьте meta.safe_block / meta.finalized_block, если вам требуется отображать подтвержденный статус.
Оценка CU для одной загрузки страницы
Каждый метод тарифицируется по его весу в CU, полученному из API тарифных планов платформы:
Вес в CU за вызов
| Метод | CU за вызов |
|---|---|
data.address_balances | 25 |
data.address_transfers | 25 |
data.tokens_batch | 10 |
Одна загрузка страницы (оценка)
1 запрос balances + 3 стр. transfer + 1 запрос(ов) tokens:batch, всего 5 вызовов, около 110 CU. Фактическое использование зависит от количества страниц и токенов.
Критерии тарификации и нетарифицируемые ответы с ошибками см. в правилах тарификации. Если вам требуется не проиндексированная история переводов, а логи из самых последних блоков, сначала прочтите руководство Свежие данные ноды или проиндексированная история, прежде чем принимать решение о переходе на eth_getLogs.
Следующие шаги
- Изучите каталог наборов данных, чтобы увидеть все наборы данных, индексируемые BlockVectra.
- Посмотрите бесплатный тариф и цены, чтобы узнать, что включено в ваш аккаунт.
- Войдите в консоль, чтобы создать API key.
Последнее обновление:
Трейсы транзакций
Восстановление деревьев вызовов исполнения транзакции: метод JSON-RPC debug_traceTransaction с разрешенными трассировщиками и проверками, а также эндпоинты Data API getTransactionTrace и getBlockTraces с границами их покрытия.
Пользовательский RPC кошелька
Добавьте RPC URL от BlockVectra в MetaMask или Rabby. Найдите Chain ID и нативные символы, настройте API key в пути URL и управляйте отдельным ключом для кошелька.