MỚI: Helius mua lại Light Protocol
danh sách kiểm tra di chuyển sang solana agave 4.2
Blog/Cập nhật

Agave 4.2: Danh sách kiểm tra di chuyển

Sản phẩm @ HeliusKiryl Miranovich trên XKiryl Miranovich trên LinkedIn
Đọc trong 5 phút

Chúng tôi đã trình bày bộ tính năng của Agave 4.2 trong bài tổng quan bản phát hành. Bài viết này tập trung vào quá trình di chuyển: những gì sẽ bị ảnh hưởng và cách xác minh tích hợp trước khi feature gate được kích hoạt.

Hầu hết những thay đổi này trả về dữ liệu sai hoặc thiếu thay vì báo lỗi, nên chúng sẽ không xuất hiện trong nhật ký ngoại lệ của bạn.

Lịch kích hoạt

Thời điểmNội dungTrạng thái
11 tháng 8, 2026Anza khuyến nghị dùng 4.2 cho mainnet; validator bắt đầu nâng cấpĐang tiến hành
Khi các node nâng cấpCơ chế ngăn cập nhật account và các thay đổi Token-2022 jsonParsed có hiệu lựcHoàn tất
Ranh giới epoch đầu tiên sau khi nâng cấpGiá trị DeactivatedStake rewardType xuất hiện trong phần thưởngHoàn tất
Epoch 1020Thời gian slot giảm từ 400ms xuống 350ms, bước đầu tiên trong bốn bước hướng đến 200msHoàn tất
Epoch 1024Thời gian slot giảm từ 350ms xuống 300msĐã lên lịch (dự kiến vào ngày 28 tháng 8)
Feature gate, chưa xác định ngàyTransaction v1 (transaction 4.096 byte)Đang chờ (chưa công bố ngày, vì vậy hãy chuẩn bị như thể tính năng sẽ được kích hoạt vào tuần tới)
Năm feature gate, chưa xác định ngàyGiảm rent, từ 6.960 lamport/byte xuống 696 theo từng bướcĐang chờ
Agave 4.3, dự kiến tháng 10 năm 2026Cơ chế đồng thuận AlpenglowBản phát hành trong tương lai

Các thay đổi gây gián đoạn trong Agave 4.2

Transaction v1 khiến toàn bộ lệnh gọi thất bại

SIMD-0296 và SIMD-0385 tăng giới hạn kích thước transaction của Solana lên 4.096 byte thông qua một định dạng transaction mới.

Không gian bổ sung cho phép các nhà phát triển chạy khối lượng công việc on-chain lớn hơn, chẳng hạn như bằng chứng ZK hoặc tuyến DEX có nhiều chặng hơn. 

Điểm cần lưu ý: chỉ một transaction v1 trong block cũng sẽ khiến getBlock thất bại cho toàn bộ block nếu lệnh gọi không đặt maxSupportedTransactionVersion: 1.

Các transaction khác cũng không được trả về; toàn bộ lệnh gọi báo lỗi. getTransaction và getTransactionsForAddress (với transactionDetails = full) cũng thất bại theo cách tương tự khi gặp bất kỳ transaction v1 nào. 

Cách khắc phục:

  • Nâng cấp SDK lên bản phát hành có thể giải mã v1. Khả năng hỗ trợ đang được bổ sung trên nhiều client. Nếu SDK của bạn chưa hỗ trợ transaction v1, hãy theo dõi ghi chú phát hành để tìm transaction v1 hoặc SIMD-0385.
    • Các client JS phổ biến: @solana/kit 8.0+, @solana/web3.js v3
    • Các crate Rust phổ biến: solana-rpc-client-api 4.2+, solana-client 4.2+, solana-transaction 4.2+, solana-compute-budget 4.2+, các crate Anza khác.
  • Đặt maxSupportedTransactionVersion: 1 cho mọi lệnh gọi getBlock, getTransaction và getTransactionsForAddress (với transactionDetails = full)
  • Đặt maxSupportedTransactionVersion: 1 trong đối tượng tùy chọn của mọi subscription transactionSubscribe trên Enhanced WebSockets (với transactionDetails được đặt thành full hoặc accounts). Các subscription sao chép từ ví dụ trong tài liệu đang đặt giá trị này thành 0; hãy tăng lên 1
Mã
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransactionsForAddress",
  "params": [
    "Vote111111111111111111111111111111111111111",
    {
      "transactionDetails": "full",
      "sortOrder": "desc",
      "filters": {
        "status": "succeeded"
      },
      "maxSupportedTransactionVersion": 1
    }
  ]
}

Tham số này khai báo phiên bản cao nhất mà client của bạn có thể xử lý. Có thể đặt tham số này ngay hôm nay một cách an toàn và nó không làm thay đổi cách các transaction legacy và v0 được trả về.

Lỗi compute budget của transaction v1

Transaction v1 lưu compute limit và priority fee trong đối tượng transactionConfig thay vì các instruction ComputeBudget. Các bảng điều khiển phí và công cụ ước tính priority fee phát hiện phí bằng cách đối chiếu những instruction đó sẽ đọc mọi transaction v1 là trả phí bằng 0 mà không báo lỗi.

Cách khắc phục:

Thay vào đó, hãy đọc các giá trị từ trường priorityFee mới trong cấu hình transaction. Lưu ý rằng trường priorityFee mới biểu thị tổng phí bằng lamport (không phải mức giá trên mỗi compute unit).

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

Các account không thay đổi sẽ ngừng phát bản cập nhật

Agave 4.2 chỉ phát sự kiện account khi account thực sự được ghi. Đối với các subscription account qua gRPC và WSS của LaserStream, điều này đồng nghĩa với số lượng sự kiện giảm khoảng 80%.

Cách khắc phục:

  1. Nếu bạn đối chiếu transaction với bản cập nhật account, hãy ngừng chờ bản cập nhật từ mọi account có thể ghi; coi việc không có bản cập nhật là "account không thay đổi". Chỉ fee payer được đảm bảo sẽ cập nhật vì luôn phải trả phí.
  2. Nếu bạn dùng tần suất cập nhật làm tín hiệu về tình trạng hoạt động, hãy bỏ bước kiểm tra đó đối với các account thường xuyên bị khóa nhưng hiếm khi thay đổi. Một account không phát sự kiện vẫn có thể hoạt động bình thường; nó ngừng phát vì không có gì thay đổi.

Giá trị rewardType mới

Các stake account hoàn tất quá trình hủy kích hoạt giờ đây nhận khoản thanh toán cuối cùng dưới một giá trị rewardType mới là DeactivatedStake trong các mảng phần thưởng getBlock và blockSubscribe. Cấu trúc đối tượng phần thưởng giống hệt Agave 4.1, vì vậy parser chỉ chấp nhận các loại phần thưởng đã biết sẽ bỏ qua khoản thanh toán DeactivatedStake mà không báo lỗi.

getInflationReward không bị ảnh hưởng; rủi ro chỉ xuất hiện khi bạn tự phân tích các mảng phần thưởng thô.

Cách khắc phục:

Thêm DeactivatedStake vào các giá trị rewardType mà parser chấp nhận, đồng thời ghi nhật ký mọi giá trị không nhận dạng được thay vì loại bỏ bản ghi.

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

Token-2022: loại bỏ một trường, mở rộng khả năng phân tích

Trong các phản hồi jsonParsed, depositConfidentialTransfer và withdrawConfidentialTransfer sẽ bỏ source và destination để dùng một trường account duy nhất. Các nhãn cũ không chính xác; mỗi instruction chỉ tác động đến một token account.

Cách khắc phục là cập nhật các parser Token-2022 để hỗ trợ trường mới.

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

Các thao tác đốt có cấp quyền, unwrapLamports, confidentialBurn và thao tác hàng loạt giờ đây được trả về dưới dạng JSON đã phân tích thay vì byte thô. 

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

Các mint chứa extension chưa được nhận dạng trước đây từng trả về một mảng extensions trống; trên Agave 4.2, mảng này sẽ được điền dữ liệu.

Nếu mã của bạn coi mảng trống là "không có extension", hãy dự kiến các giá trị sẽ bắt đầu xuất hiện tại đó.

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

Các hằng số thời gian slot đã lỗi thời

Mainnet chạy slot 350ms kể từ epoch 1020. Lần giảm tiếp theo xuống 300ms được lên lịch cho epoch 1024 (dự kiến vào ngày 28 tháng 8), với mục tiêu là 200ms. Các hằng số 400ms được mã hóa cứng trong phép tính chuyển slot thành thời gian hiện đang sai lệch 12,5% và sẽ tiếp tục lệch hơn sau mỗi bước. 

Cách khắc phục:

Hãy suy ra thời gian từ timestamp của block hoặc cho phép cấu hình giá trị này, đồng thời bảo đảm indexer có thể theo kịp khi các block bắt đầu đến nhanh hơn.

Xác minh bạn đã hoàn tất

Để xác minh đã hoàn tất, hãy chạy quy trình kiểm tra bằng agent và xác nhận các dependency.

Chạy quy trình kiểm tra bằng agent

Sao chép skill dưới đây vào coding agent của bạn. 

Đối với Claude Code, hãy lưu dưới tên .claude/skills/agave-42-readiness/SKILL.md.

Đối với các agent khác, hãy dán nội dung này làm prompt tác vụ.

Câu lệnh
---
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
Mở rộngThu gọn

Xác nhận dependency của stream

Rust: yellowstone-grpc-client 13.3.0 và yellowstone-grpc-proto 12.6.0, khai báo cả hai, sau đó chạy cargo update. 

Go: tạo lại từ các proto Yellowstone mới nhất và chạy solana-storage-proto. 

LaserStream SDK: JS 0.8.4, Rust 0.6.3 và Go 0.2.0 trở lên bao gồm proto hỗ trợ v1. Bạn không cần thay đổi subscription và bộ lọc trên bất kỳ luồng nào.

Câu hỏi

Nếu tích hợp của bạn hoạt động khác trên 4.2 và bài viết này không giải thích được nguyên nhân, hãy liên hệ qua Discord hoặc bộ phận hỗ trợ. Để xem phân tích chi tiết theo từng tính năng, hãy đọc bài tổng quan 4.2.

Đăng ký nhận tin từ Helius

Luôn cập nhật những thông tin mới nhất về phát triển Solana và nhận thông báo khi chúng tôi đăng bài