НОВОЕ: Helius приобретает Light Protocol
чек-лист миграции Solana Agave 4.2
Блог/Обновления

Agave 4.2: чек-лист миграции

Продуктовая команда @ HeliusKiryl Miranovich в XKiryl Miranovich в LinkedIn
Чтение: 5 мин

Мы рассмотрели набор функций Agave 4.2 в нашем обзоре релиза. Эта публикация посвящена миграции: что перестанет работать и как проверить интеграцию до активации гейта функции.

Большинство этих изменений приводят к возврату неверных или отсутствующих данных, а не ошибок. Поэтому они не появятся в ваших логах исключений.

График активации

КогдаЧтоСтатус
11 августа 2026 г.Anza рекомендует версию 4.2 для mainnet; начинается обновление валидаторовВыполняется
По мере обновления нодВступают в силу подавление обновлений аккаунтов и изменения Token-2022 jsonParsedГотово
Первая граница эпохи после обновленияВ вознаграждениях появляется значение DeactivatedStake rewardTypeГотово
Эпоха 1020Время слота сокращается с 400 мс до 350 мс — первый из четырех этапов перехода к 200 мсГотово
Эпоха 1024Время слота сокращается с 350 мс до 300 мсЗапланировано (ожидается 28 августа)
Гейт функции, дата уточняетсяТранзакции v1 (транзакции размером 4 096 байт)Ожидается (дата не объявлена, поэтому подготовьтесь так, будто активация произойдет на следующей неделе)
Пять гейтов функций, дата уточняетсяСнижение ренты с 6 960 лампортов за байт до 696Ожидается
Agave 4.3, ориентировочно октябрь 2026 г.Консенсус AlpenglowБудущий релиз

Критические изменения в Agave 4.2

Транзакция v1 приводит к сбою всего вызова

SIMD-0296 и SIMD-0385 увеличивают максимальный размер транзакции Solana до 4 096 байт благодаря новому формату транзакций.

Дополнительное пространство позволяет разработчикам выполнять более крупные ончейн-нагрузки, например ZK-доказательства или маршруты DEX с большим числом этапов. 

Проблема в том, что одна транзакция v1 в блоке нарушает работу getBlock для всего блока, если в вызове не задан maxSupportedTransactionVersion: 1.

Остальные транзакции также не возвращаются — весь вызов завершается ошибкой. getTransaction и getTransactionsForAddress (с transactionDetails = full) таким же образом завершаются ошибкой при любой транзакции v1. 

Как исправить:

  • Обновите SDK до версии, которая декодирует v1. Поддержка постепенно появляется в разных клиентах. Если ваш SDK еще не поддерживает транзакции v1, следите за упоминаниями transaction v1 или SIMD-0385 в примечаниях к релизам.
    • Популярные JS-клиенты: @solana/kit 8.0+, @solana/web3.js v3
    • Популярные Rust-крейты: solana-rpc-client-api 4.2+, solana-client 4.2+, solana-transaction 4.2+, solana-compute-budget 4.2+, другие крейты Anza.
  • Задайте maxSupportedTransactionVersion: 1 в каждом вызове getBlock, getTransaction и getTransactionsForAddress (с transactionDetails = full)
  • Задайте maxSupportedTransactionVersion: 1 в объекте параметров каждой подписки transactionSubscribe в Enhanced WebSockets (при этом transactionDetails должен быть равен full или accounts). В подписках, скопированных из примеров документации, задано значение 0; обновите его до 1
Код
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransactionsForAddress",
  "params": [
    "Vote111111111111111111111111111111111111111",
    {
      "transactionDetails": "full",
      "sortOrder": "desc",
      "filters": {
        "status": "succeeded"
      },
      "maxSupportedTransactionVersion": 1
    }
  ]
}

Параметр указывает максимальную версию, которую поддерживает ваш клиент. Его можно безопасно задать уже сейчас: он не влияет на возврат устаревших транзакций и транзакций v0.

Сбои бюджета вычислений в транзакциях v1

Транзакция v1 хранит лимит вычислений и приоритетную комиссию в объекте transactionConfig, а не в инструкциях ComputeBudget. Панели мониторинга комиссий и инструменты оценки приоритетной комиссии, которые определяют комиссии по совпадению этих инструкций, без ошибок показывают для каждой транзакции v1 нулевую комиссию.

Как исправить:

Вместо этого считывайте значения из нового поля priorityFee в конфигурации транзакции. Учтите, что новое поле priorityFee содержит общую комиссию в лампортах, а не цену за единицу вычислений.

Код
"message": {
  "instructions": ["… no ComputeBudget instruction here …"],
  "recentBlockhash": "...",
  "transactionConfig": {
    "computeUnitLimit": 200000,
    "heapSize": null,
    "loadedAccountsDataSizeLimit": 200000,
    "priorityFee": 50000
  }
}

Неизмененные аккаунты перестают отправлять обновления

Agave 4.2 отправляет события аккаунта только при фактической записи в аккаунт. Для подписок на аккаунты через LaserStream gRPC и WSS это означает примерно на 80% меньше событий.

Как исправить:

  1. Если вы сопоставляете транзакции с обновлениями аккаунтов, перестаньте ждать обновления от каждого аккаунта с правом записи. Считайте отсутствие обновления признаком того, что «аккаунт не изменился». Обновление гарантировано только для плательщика комиссии, поскольку он всегда оплачивает комиссию.
  2. Если вы используете частоту обновлений как индикатор работоспособности, уберите эту проверку для аккаунтов, которые часто блокируются, но редко изменяются. Отсутствие событий не означает проблему: аккаунт перестал их отправлять, потому что ничего не изменилось.

Новое значение rewardType

Аккаунты стейкинга, завершившие деактивацию, теперь получают последнюю выплату с новым значением rewardType — DeactivatedStake — в массивах вознаграждений getBlock и blockSubscribe. Структура объекта вознаграждения совпадает с Agave 4.1, поэтому парсер, принимающий только уже известные ему типы вознаграждений, без ошибки пропустит выплаты DeactivatedStake.

getInflationReward не затронут. Риск возникает только там, где вы самостоятельно разбираете необработанные массивы вознаграждений.

Как исправить:

Добавьте DeactivatedStake в список значений rewardType, которые принимает ваш парсер. Логируйте все нераспознанные значения вместо удаления записи.

Код
{
  "pubkey": "...",
  "lamports": 10000000,
  "postBalance": 50000000000,
  "rewardType": "DeactivatedStake",
  "commission": null,
  "commissionBps": 500
}

Token-2022: одно поле удалено, возможности парсинга расширены

В ответах jsonParsed поля depositConfidentialTransfer и withdrawConfidentialTransfer лишаются source и destination в пользу единого поля account. Старые метки были неверными: каждая инструкция затрагивает один токен-аккаунт.

Чтобы устранить проблему, обновите парсеры Token-2022 для поддержки нового поля.

Код
{
  "parsed": {
    "type": "depositConfidentialTransfer",
    "info": {
      "account": "6XVfUq9jZQtBfqcm1Rz8fBhViyoyzWiEUAaWnQ9AmXeR",
      "mint": "8fJ7bCZo2vZ3vAnyCBQgZuLYuTX1qPnKvsMKotk92B2J",
      "amount": 42,
      "decimals": 9,
      "owner": "F7yLk3s2iZDPBBvSNbXAaKPBnQ9vXqSSSzYUCGeUFhXn"
    }
  }
}

Операции permissioned burn, unwrapLamports, confidentialBurn и пакетные операции теперь возвращаются как разобранный JSON, а не необработанные байты. 

Код
{
  "parsed": {
    "type": "unwrapLamports",
    "info": {
      "source": "9rr9Xh6PXPKcVqbCB1qxGDWRUJJAdguqcUqVuUqEjqQK",
      "destination": "BhU2wDgmvvMNC1vTSU4aG7BvW26MoTMSDL63hcMqziGL",
      "amount": "1000000",
      "authority": "F7yLk3s2iZDPBBvSNbXAaKPBnQ9vXqSSSzYUCGeUFhXn"
    }
  }
}

Раньше минты с нераспознанными расширениями возвращали пустой массив extensions. В Agave 4.2 этот массив заполняется.

Если ваш код интерпретирует пустой массив как «расширений нет», учтите, что теперь в нем начнут появляться значения.

Код
"extensions": [
  {
    "extension": "transferFeeConfig",
    "state": {
      "transferFeeConfigAuthority": "...",
      "withdrawWithheldAuthority": "...",
      "withheldAmount": 0,
      "olderTransferFee": {...},
      "newerTransferFee": {...}
    }
  },
  {
    "extension": "permissionedBurnConfig",
    "state": {
      "authority": "3nGhQzXCzoDDvW9pkg8fVLGDrJc23uwFz7qzW26MoTMS"
    }
  }
]

Константы времени слота устарели

Начиная с эпохи 1020, в mainnet используются слоты длительностью 350 мс. Следующее сокращение до 300 мс запланировано на эпоху 1024 (ожидается 28 августа), а целевое значение составляет 200 мс. Жестко заданные константы 400 мс в расчетах преобразования слотов во время уже сейчас дают погрешность 12,5%, и на каждом следующем этапе она будет расти. 

Как исправить:

Рассчитывайте время по временным меткам блоков или сделайте его настраиваемым. Убедитесь, что ваш индексатор справится с более частым поступлением блоков.

Проверьте готовность

Чтобы проверить готовность, запустите аудит с помощью агента и проверьте зависимости.

Запустите аудит с помощью агента

Скопируйте приведенный ниже навык в свой агент для написания кода. 

Для Claude Code сохраните его как .claude/skills/agave-42-readiness/SKILL.md.

Для других агентов вставьте его как промпт задачи.

Запрос
---
name: agave-42-readiness
description: Audit this repository for Solana Agave 4.2 breaking changes. Use when asked to check Agave 4.2 readiness, migrate for transaction v1, or audit Solana RPC/streaming integration code.
---

# Agave 4.2 Readiness Audit

Audit the repository for the five Agave 4.2 breaking changes below. For each, locate the relevant code, judge whether it is affected, and report file:line with a verdict of PASS, FAIL, or NEEDS REVIEW. Do not modify code unless asked to fix findings.

## Check 1: Transaction v1 opt-in (highest priority)
Find every `getBlock`, `getTransaction`, and `getTransactionsForAddress` (with `transactionDetails = full`) call, plus every Enhanced WebSockets `transactionSubscribe` subscription. Search all languages, raw JSON-RPC request bodies (`"method": "getBlock"`, `"method": "transactionSubscribe"`), and SDK wrappers (`connection.getBlock`, `connection.getParsedTransaction`, `rpc_client.get_block`, `client.GetTransaction`, and similar).
- FAIL if `maxSupportedTransactionVersion` is absent or set to 0. Once the v1 feature gate activates, these calls fail on v1 transactions with JSON-RPC error code -32015: `"Transaction version (1) is not supported by the requesting client. Please use \"maxSupportedTransactionVersion\" in your request."` Also grep for `-32015` in logs and error handlers; hits mean the project has already been failing on versioned transactions.
- Also FAIL if the project deserializes raw transaction bytes (custom indexer, signer, relayer) and the decoder does not handle the v1 layout: version byte 0x81 (decimal 129, vs 0x80 for v0), signatures at the END of the transaction instead of the front, compute budget carried in a header config mask instead of instructions.
- Correct fix order: upgrade the Solana SDK to a v1-capable version (JS: @solana/kit 8.0+ or @solana/web3.js v3), then set `maxSupportedTransactionVersion: 1`. Flag if the parameter is set but the installed SDK version predates v1 support.
- Also FAIL if priority fee or compute budget extraction scans for ComputeBudget program instructions (`ComputeBudget111111111111111111111111111111`, `setComputeUnitPrice`, `setComputeUnitLimit`). A v1 transaction contains no such instructions; its values live in a `transactionConfig` object in the message. Scanning code reads every v1 transaction as paying zero priority fee.
- Units trap, FAIL even when the code reads `transactionConfig`: legacy/v0 `setComputeUnitPrice` is **micro-lamports per compute unit**; v1 `priorityFee` is a **total in lamports**. Code that ports the old `price × computeUnitLimit ÷ 1e6` math onto the new field misreports fees. `priorityFee` needs no multiplication. Null fields mean the sender did not set them.

Example v1 message:
```
"message": {
  "instructions": ["… no ComputeBudget instruction here …"],
  "recentBlockhash": "...",
  "transactionConfig": {
    "computeUnitLimit": 200000,
    "heapSize": null,
    "loadedAccountsDataSizeLimit": 200000,
    "priorityFee": 50000
  }
}
```
`"priorityFee": 50000` = this transaction pays 50,000 lamports total. Legacy and v0 messages omit `transactionConfig` entirely, so its presence (or `"version": 1` on the enclosing transaction) identifies v1.

## Check 2: rewardType parsed as a closed enum
Find code reading `rewardType` from `getBlock`, `blockSubscribe`, or Geyser/gRPC reward arrays. Search for the existing values too (`"Fee"`, `"Rent"`, `"Staking"`, `"Voting"` in JSON paths; `RewardType::` in Rust; reward-type enums in generated gRPC code).
- 4.2 adds the value `DeactivatedStake` — same capitalized casing as the existing JSON values (`"Staking"`, not `"staking"`). A reward entry looks like:
```
{
  "pubkey": "...",
  "lamports": 10000000,
  "postBalance": 50000000000,
  "rewardType": "DeactivatedStake",
  "commission": null
}
```
- FAIL if unknown values throw, or fall into a match/switch arm or if/else chain that drops the record without logging: a Rust `match` without a logging wildcard arm, a TS `switch` whose `default` is silent, a lookup table where a missing key skips the entry. Closed parsers lose every deactivating stake account's final payout with no error.
- FAIL if a deserialization enum (serde, protobuf mapping, Zod/io-ts schema) rejects unknown reward-type strings; the whole reward array or block record errors out, not just one entry.
- PASS requires unknown values to be preserved or logged. If the project aggregates staking yield by filtering `rewardType == "Staking"`, add a NEEDS REVIEW noting the team must decide whether `DeactivatedStake` payouts belong in totals; they are final payouts, not recurring yield.

## Check 3: Transaction-to-account-update matching
Find logic that correlates transactions with account update notifications. Search for `accountSubscribe`, Yellowstone/LaserStream `SubscribeRequest` account filters, and code that builds a pending set of a transaction's writable accounts and waits to check them off as updates arrive.
- FAIL if it assumes every writable account in a transaction produces an update. On 4.2, an account that was write-locked but not modified emits nothing; logic awaiting the full set hangs forever. Typical patterns: a countdown/completion latch over `message.accountKeys` writable entries, a timeout that treats a missing update as an error, a reconciler that re-fetches "late" accounts. The fee payer is the only account guaranteed to update.
- The correct interpretation of a missing update is "this account did not change"; its pre-transaction state is still current.
- Also FAIL liveness or health checks that treat update frequency as a signal, and flag alert thresholds calibrated to pre-4.2 volume; account-subscription event counts drop roughly 80% on 4.2.

## Check 4: Token-2022 jsonParsed changes
Find parsers reading Token-2022 `jsonParsed` output: instruction types `depositConfidentialTransfer` and `withdrawConfidentialTransfer`, and mint-account `extensions` arrays.
- FAIL if confidential-transfer parsers read `source` or `destination`; 4.2 replaces both with a single `account` field. New shape:
```
"info": {
  "account": "...",   // was source + destination
  "mint": "...",
  "amount": 42,
  "decimals": 9,
  "owner": "..."      // multisig: "multisigOwner" + "signers" array instead
}
```
- New instruction types now come back parsed instead of raw: `unwrapLamports`, `confidentialBurn`, permissioned burn, and batch operations. FAIL if code assumes these arrive as raw base64/base58 data, and flag `unwrapLamports` parsers that require `info.amount` or treat it as a number: it is a **string** and is **omitted entirely** when the instruction unwraps the full balance.
- Extensions array: on 4.1, one unrecognized extension type on a mint made the node return `"extensions": []`, hiding every extension including known ones. On 4.2 the full array returns, each entry as `{"extension": "<name>", "state": {...}}`, with still-undecodable ones as `{"extension": "unparseableExtension"}`. FAIL if code treats an empty array as proof of no extensions, or throws on extension names it does not recognize (`scaledUiAmountConfig`, `pausableConfig`, `permissionedBurnConfig`, `unparseableExtension`, and future values).

## Check 5: Slot timing and streaming dependencies
- FAIL on hardcoded slot-duration constants: `400`, `0.4`, `400_000` microseconds, `MS_PER_SLOT`, `SLOT_DURATION`, `DEFAULT_MS_PER_SLOT`, or slot-to-time math like `slots * 400`. Mainnet is 350ms, cutting to 300ms on Aug 28, 2026, stepping toward 200ms. Constants inherited from an SDK count as hardcoded. PASS requires timing derived from block timestamps (e.g. `getBlockTime` deltas over a few thousand slots) or a config value with a documented update path.
- Also flag capacity assumptions keyed to block rate: batch sizes, poll intervals, and queue depths sized for 400ms blocks fall behind as slots shorten.
- In Cargo.toml/Cargo.lock: FAIL if `yellowstone-grpc-client` < 13.3.0 or `yellowstone-grpc-proto` < 12.6.0 (first proto carrying transaction v1). Recommend client 13.3.0, proto 12.6.0, both declared.
- For Go gRPC clients: flag if generated protos predate transaction v1; regenerate from latest Yellowstone protos and solana-storage-proto.
- If the project uses the Helius LaserStream SDK (package names `helius-laserstream`, `laserstream` in dependencies): FAIL if below JS 0.8.4, Rust 0.6.3, or Go 0.2.0 (first releases with the v1-capable proto).

## Report format
Output a table: check, verdict, file:line references, one-line remediation. End with an overall verdict (READY / NOT READY) and the ordered fix list. Full migration guide: https://www.helius.dev/blog/agave-4-2-migration-checklist
РазвернутьСвернуть

Проверьте зависимости стримов

Rust: yellowstone-grpc-client 13.3.0 и yellowstone-grpc-proto 12.6.0 — обе зависимости должны быть объявлены, после чего выполните cargo update. 

Go: сгенерируйте код заново на основе последних proto-файлов Yellowstone и выполните solana-storage-proto. 

LaserStream SDK: версии JS 0.8.4, Rust 0.6.3 и Go 0.2.0 или новее включают proto с поддержкой v1. Подписки и фильтры не требуют изменений ни в одном из вариантов.

Вопросы

Если ваша интеграция ведет себя иначе в версии 4.2 и эта публикация не объясняет причину, свяжитесь с нами в Discord или через службу поддержки. Подробное описание функций доступно в обзоре версии 4.2.

Подпишитесь на Helius

Следите за новостями разработки на Solana и получайте уведомления о наших публикациях