Примеры NEAR Data
Примеры
NEAR Data возвращает каждый блок полностью гидратированным одним JSON-документом — header плюс per-shard chunks, receipts, результаты исполнения и state changes, — так что один curl уже даёт всё необходимое, чтобы отфильтровать нужный контракт без второго запроса.
Все shell-примеры ниже работают на публичных NEAR Data-хостах как есть. Если в shell задан FASTNEAR_API_KEY, они автоматически добавляют bearer header; если переменная не задана, они переходят на публичный неаутентифицированный путь.
На каком блоке NEAR сейчас?
/v0/last_block/final отдаёт 302-редирект на текущий финализированный блок. Прежде чем фильтровать по конкретному контракту, полезно увидеть, как выглядит один блок на уровне протокола: транзакции приходят с разбивкой по shard, поэтому общее число транзакций в блоке — это сумма по shards, а не одно поле верхнего уровня.
AUTH_HEADER=()
if [ -n "${FASTNEAR_API_KEY:-}" ]; then AUTH_HEADER=(-H "Authorization: Bearer $FASTNEAR_API_KEY"); fi
curl -sL "https://mainnet.neardata.xyz/v0/last_block/final" \
"${AUTH_HEADER[@]}" \
| jq '{
height: .block.header.height,
timestamp_nanosec: .block.header.timestamp_nanosec,
txs_per_shard: [.shards[] | {shard_id, tx_count: (.chunk.transactions | length)}],
total_txs: ([.shards[].chunk.transactions[]?] | length)
}'Живой блок показывает 9 shards и горсть транзакций, распределённых по ним — большинство shards в любом блоке пустые, а активность концентрируется на тех shards, где живут загруженные контракты. timestamp_nanosec — это Unix-время в наносекундах (делите на 1e9, чтобы получить секунды). С этим одним вызовом у вас уже есть всё необходимое для дальнейшего копания — примеры фильтрации ниже просто применяют jq к тому же ответу.
Был ли мой контракт затронут в последнем финализированном блоке?
/v0/last_block/final отдаёт 302-редирект на текущий финализированный блок. Контракт может проявиться либо в transactions chunk (когда он receiver_id), либо в receipts (когда прилетает cross-shard-вызов), поэтому один проход jq по shards покрывает оба случая.
TARGET_CONTRACT=intents.near
AUTH_HEADER=()
if [ -n "${FASTNEAR_API_KEY:-}" ]; then AUTH_HEADER=(-H "Authorization: Bearer $FASTNEAR_API_KEY"); fi
curl -sL "https://mainnet.neardata.xyz/v0/last_block/final" \
"${AUTH_HEADER[@]}" \
| jq --arg contract "$TARGET_CONTRACT" '{
height: .block.header.height,
contract: $contract,
touched_shards: [
.shards[] | {
shard_id,
txs: [.chunk.transactions[]? | select(.transaction.receiver_id == $contract) | .transaction.hash],
receipts: [.chunk.receipts[]? | select(.receiver_id == $contract) | .receipt_id]
} | select((.txs | length) + (.receipts | length) > 0)
]
}'touched_shards: [] — полный ответ для тихого блока. Непустой список называет shards, где контракт появился, и отдаёт конкретные tx-хеши или receipt_id — передавайте хеш в Transactions API, когда нужна человекочитаемая история. Receipts без парных txs — это нормально: cross-contract-вызов приходит как incoming receipt в этом блоке, даже если исходная транзакция была раньше.
Увидел ли я активность в optimistic-режиме, и догнала ли её finality?
Optimistic-блоки ходят по /v0/block_opt/{height} примерно на секунду впереди /v0/block/{height}. Цикл мониторинга может действовать по optimistic-сигналу и ожидать, что тот же ответ придёт на финализированный эндпоинт через один блок — если только стресс сети не расширит разрыв, и тогда финализированный fetch вернёт null, а вы подождёте.
TARGET_CONTRACT=intents.near
AUTH_HEADER=()
if [ -n "${FASTNEAR_API_KEY:-}" ]; then AUTH_HEADER=(-H "Authorization: Bearer $FASTNEAR_API_KEY"); fi
count_touches() {
jq --arg contract "$1" '
[.shards[]
| ([.chunk.transactions[]? | select(.transaction.receiver_id == $contract)] | length)
+ ([.chunk.receipts[]? | select(.receiver_id == $contract)] | length)]
| add // 0'
}
OPT_LOCATION="$(
curl -s -D - -o /dev/null "${AUTH_HEADER[@]}" "https://mainnet.neardata.xyz/v0/last_block/optimistic" \
| awk 'tolower($1) == "location:" {print $2}' | tr -d '\r'
)"
OPT_HEIGHT="${OPT_LOCATION##*/}"
echo "optimistic @ $OPT_HEIGHT: $(curl -s "https://mainnet.neardata.xyz$OPT_LOCATION" \
"${AUTH_HEADER[@]}" | count_touches "$TARGET_CONTRACT") touches"
FINAL="$(curl -s "https://mainnet.neardata.xyz/v0/block/$OPT_HEIGHT" \
"${AUTH_HEADER[@]}")"
if [ "$(printf '%s' "$FINAL" | jq 'type')" = '"null"' ]; then
echo "finalized @ $OPT_HEIGHT: not caught up yet"
else
echo "finalized @ $OPT_HEIGHT: $(printf '%s' "$FINAL" | count_touches "$TARGET_CONTRACT") touches"
fiНа здоровом mainnet оба счётчика совпадают в пределах секунды. Ценность — в самом шаблоне: optimistic-поток даёт ответ, на который можно реагировать сразу, а финализированный приходит через блок как надёжное подтверждение.
Какой shard действительно изменил состояние моего контракта?
В большинстве финализированных блоков нет мутации состояния ни для одного конкретного контракта — активность разрежена и привязана к shard. Идите назад от финализированной головы, пока состояние контракта реально не изменится, и откройте этот shard для payload мутации. Вызов уровня блока говорит, на каком shard это случилось; вызов уровня shard — как.
TARGET_CONTRACT=intents.near
AUTH_HEADER=()
if [ -n "${FASTNEAR_API_KEY:-}" ]; then AUTH_HEADER=(-H "Authorization: Bearer $FASTNEAR_API_KEY"); fi
HEAD="$(curl -sL "https://mainnet.neardata.xyz/v0/last_block/final" \
"${AUTH_HEADER[@]}" | jq '.block.header.height')"
FOUND_HEIGHT=""
FOUND_SHARD=""
for OFFSET in $(seq 0 15); do
H=$((HEAD - OFFSET))
SHARD="$(curl -s "https://mainnet.neardata.xyz/v0/block/$H" \
"${AUTH_HEADER[@]}" \
| jq -r --arg contract "$TARGET_CONTRACT" '
.shards[]
| select([.state_changes[]? | select(.change.account_id? == $contract)] | length > 0)
| .shard_id
' | head -1)"
if [ -n "$SHARD" ]; then
FOUND_HEIGHT=$H; FOUND_SHARD=$SHARD
break
fi
done
if [ -z "$FOUND_HEIGHT" ]; then
echo "no state mutation for $TARGET_CONTRACT in the last 16 finalized blocks"
else
curl -s "https://mainnet.neardata.xyz/v0/block/$FOUND_HEIGHT/shard/$FOUND_SHARD" \
"${AUTH_HEADER[@]}" \
| jq --arg contract "$TARGET_CONTRACT" --argjson height "$FOUND_HEIGHT" --argjson shard_id "$FOUND_SHARD" '{
height: $height,
shard_id: $shard_id,
state_changes: [.state_changes[] | select(.change.account_id? == $contract) | {type, cause: (.cause | keys[0])}][0:3],
execution_outcomes: [.receipt_execution_outcomes[] | select(.execution_outcome.outcome.executor_id == $contract) | {receipt_id: .execution_outcome.id, status: (.execution_outcome.outcome.status | keys[0])}][0:3]
}'
fiНа mainnet intents.near живёт на shard 7, поэтому обход назад обычно попадает в цель за несколько блоков. Payload shard называет конкретные типы state-change (account_update, data_update и т. п.) и результаты receipt, которые их породили, — shard-локальное доказательство без догадок. Для менее активных контрактов расширьте диапазон OFFSET.
Когда расширить поверхность
- Используйте Transactions API, когда у вас уже есть
tx_hashи нужна человекочитаемая история транзакции. - Используйте RPC Reference, когда следующий вопрос касается точной протокольной семантики receipt или блока.
- Используйте Block Headers, когда нужна только динамика head/finality, а не проверка contract-touch.