NOUVEAU : Helius acquiert Light Protocol
checklist de migration vers Solana Agave 4.2
Blog/Actualités

Agave 4.2 : la checklist de migration

Produit chez HeliusKiryl Miranovich sur XKiryl Miranovich sur LinkedIn
5 min de lecture

Nous avons présenté les fonctionnalités d’Agave 4.2 dans notre aperçu de la version. Cet article traite de la migration : ce qui cesse de fonctionner et comment vérifier votre intégration avant l’activation de la porte de fonctionnalité.

La plupart de ces changements renvoient des données incorrectes ou manquantes plutôt que des erreurs. Ils n’apparaîtront donc pas dans vos journaux d’exceptions.

Calendrier d’activation

QuandQuoiStatut
11 août 2026Anza recommande la version 4.2 pour le réseau principal ; les mises à niveau des validateurs commencentEn cours
À mesure que les nœuds sont mis à niveauLa suppression des mises à jour de comptes et les changements Token-2022 de jsonParsed prennent effetTerminé
Première limite d’époque après la mise à niveauLa valeur DeactivatedStake rewardType apparaît dans les récompensesTerminé
Époque 1020La durée des slots passe de 400 ms à 350 ms, première des quatre étapes vers 200 msTerminé
Époque 1024La durée des slots passe de 350 ms à 300 msPlanifié (prévu le 28 août)
Porte de fonctionnalité, date à déterminerTransaction v1 (transactions de 4 096 octets)En attente (aucune date annoncée, préparez-vous donc comme si elle devait être activée la semaine prochaine)
Cinq portes de fonctionnalités, date à déterminerRéduction du loyer, de 6 960 lamports/octet à 696 par paliersEn attente
Agave 4.3, prévue pour octobre 2026Consensus AlpenglowVersion future

Changements incompatibles d’Agave 4.2

Transaction v1 fait échouer l’appel entier

SIMD-0296 et SIMD-0385 portent la taille maximale des transactions Solana à 4 096 octets grâce à un nouveau format de transaction.

Cet espace supplémentaire permet aux développeurs d’exécuter des charges de travail on-chain plus importantes, comme des preuves ZK ou des routes DEX comportant davantage d’étapes. 

Le problème : une seule transaction v1 dans un bloc fait échouer getBlock pour l’ensemble du bloc si l’appel ne définit pas maxSupportedTransactionVersion: 1.

Les autres transactions ne sont pas renvoyées non plus ; l’appel entier échoue. getTransaction et getTransactionsForAddress (avec transactionDetails = full) échouent de la même manière pour toute transaction v1. 

Comment corriger le problème :

  • Mettez à niveau votre SDK vers une version capable de décoder v1. La prise en charge est en cours de déploiement dans les différents clients. Si votre SDK ne prend pas encore en charge transaction v1, surveillez ses notes de version pour transaction v1 ou SIMD-0385.
    • Clients JS populaires : @solana/kit 8.0+, @solana/web3.js v3
    • Crates Rust populaires : solana-rpc-client-api 4.2+, solana-client 4.2+, solana-transaction 4.2+, solana-compute-budget 4.2+, autres crates Anza.
  • Définissez maxSupportedTransactionVersion: 1 pour chaque appel à getBlock, getTransaction et getTransactionsForAddress (avec transactionDetails = full)
  • Définissez maxSupportedTransactionVersion: 1 dans l’objet d’options de chaque abonnement transactionSubscribe d’Enhanced WebSockets (avec transactionDetails défini sur full ou accounts). Dans les abonnements copiés depuis les exemples de la documentation, cette valeur est définie sur 0 ; passez-la à 1
Code
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransactionsForAddress",
  "params": [
    "Vote111111111111111111111111111111111111111",
    {
      "transactionDetails": "full",
      "sortOrder": "desc",
      "filters": {
        "status": "succeeded"
      },
      "maxSupportedTransactionVersion": 1
    }
  ]
}

Ce paramètre indique la version la plus élevée que votre client peut traiter. Vous pouvez le définir dès aujourd’hui sans risque ; cela ne change rien à la façon dont les transactions legacy et v0 sont renvoyées.

Échecs du budget de calcul avec Transaction v1

Une transaction v1 stocke sa limite de calcul et ses frais de priorité dans l’objet transactionConfig plutôt que dans des instructions ComputeBudget. Les tableaux de bord de frais et les estimateurs de frais de priorité qui détectent les frais en recherchant ces instructions considèrent chaque transaction v1 comme ne payant aucuns frais, sans générer d’erreur.

Comment corriger le problème :

Lisez plutôt les valeurs dans le nouveau champ priorityFee de la configuration de la transaction. Notez que le nouveau champ priorityFee indique des frais totaux en lamports, et non un prix par unité de calcul.

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

Les comptes inchangés cessent d’émettre des mises à jour

Agave 4.2 n’émet désormais des événements de compte que lorsqu’un compte est effectivement modifié. Pour les abonnements de comptes gRPC et WSS de LaserStream, cela représente environ 80 % d’événements en moins.

Comment corriger le problème :

  1. Si vous associez des transactions à des mises à jour de comptes, cessez d’attendre une mise à jour de chaque compte accessible en écriture ; considérez l’absence de mise à jour comme signifiant « le compte n’a pas changé ». Seul le payeur des frais est assuré d’être mis à jour, puisqu’il paie toujours les frais.
  2. Si vous utilisez la fréquence des mises à jour comme indicateur d’intégrité, supprimez cette vérification pour les comptes souvent verrouillés mais rarement modifiés. Un compte silencieux est sain ; il a cessé d’émettre parce que rien n’a changé.

Une nouvelle valeur rewardType

Les comptes de staking qui terminent leur désactivation reçoivent désormais leur paiement final sous une nouvelle valeur rewardType, DeactivatedStake, dans les tableaux de récompenses getBlock et blockSubscribe. La structure de l’objet de récompense est identique à celle d’Agave 4.1. Un parseur qui n’accepte que les types de récompenses qu’il connaît déjà ignorera donc les paiements DeactivatedStake sans générer d’erreur.

getInflationReward n’est pas affecté ; le risque ne concerne que les cas où vous analysez vous-même les tableaux de récompenses bruts.

Comment corriger le problème :

Ajoutez DeactivatedStake aux valeurs rewardType acceptées par votre parseur et consignez toute valeur non reconnue au lieu d’ignorer l’enregistrement.

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

Token-2022 : un champ supprimé, une analyse étendue

Dans les réponses jsonParsed, depositConfidentialTransfer et withdrawConfidentialTransfer perdent les champs source et destination au profit d’un seul champ account. Les anciens libellés étaient incorrects ; chaque instruction concerne un seul compte de token.

Pour corriger le problème, mettez à jour les parseurs Token-2022 afin qu’ils prennent en charge le nouveau champ.

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

Les opérations permissioned burn, unwrapLamports, confidentialBurn et les opérations par lots sont désormais renvoyées sous forme de JSON analysé plutôt que d’octets bruts. 

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

Les mints comportant des extensions auparavant non reconnues renvoyaient un tableau extensions vide ; avec Agave 4.2, ce tableau est renseigné.

Si votre code interprète un tableau vide comme « aucune extension », attendez-vous à ce que des valeurs commencent à y apparaître.

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

Les constantes de durée des slots sont obsolètes

Depuis l’époque 1020, les slots du réseau principal durent 350 ms. La prochaine réduction, à 300 ms, est prévue pour l’époque 1024 (le 28 août selon les estimations), avec un objectif final de 200 ms. Les constantes de 400 ms codées en dur dans les calculs de conversion des slots en temps présentent aujourd’hui un écart de 12,5 %, qui augmentera à chaque étape. 

Comment corriger le problème :

Déduisez la durée à partir des horodatages des blocs ou rendez-la configurable, et assurez-vous que votre indexeur pourra suivre le rythme lorsque les blocs commenceront à arriver plus vite.

Vérifiez que votre migration est terminée

Pour vérifier que votre migration est terminée, exécutez l’audit de l’agent et confirmez vos dépendances.

Exécutez l’audit de l’agent

Copiez la skill ci-dessous dans votre agent de programmation. 

Pour Claude Code, enregistrez-la sous .claude/skills/agave-42-readiness/SKILL.md.

Pour les autres agents, collez-la comme prompt de tâche.

Prompt
---
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
DévelopperRéduire

Confirmez les dépendances des flux

Rust : yellowstone-grpc-client 13.3.0 et yellowstone-grpc-proto 12.6.0, tous deux déclarés, puis cargo update. 

Go : régénérez à partir des derniers protos Yellowstone et de solana-storage-proto. 

SDK LaserStream : les versions JS 0.8.4, Rust 0.6.3 et Go 0.2.0 ou ultérieures incluent le proto compatible avec v1. Aucun changement n’est nécessaire pour vos abonnements et vos filtres, quel que soit le parcours.

Questions

Si votre intégration se comporte différemment avec la version 4.2 et que cet article ne l’explique pas, contactez-nous sur Discord ou via le support. Pour une présentation détaillée des fonctionnalités, consultez l’aperçu de la version 4.2.

Abonnez-vous à Helius

Suivez les dernières actualités du développement sur Solana et recevez une notification à chaque publication