
Agave 4.2: 마이그레이션 체크리스트
릴리스 개요에서 Agave 4.2의 기능 세트를 살펴봤습니다. 이 글에서는 마이그레이션을 다룹니다. 어떤 부분에서 문제가 발생하는지, 기능 게이트가 활성화되기 전에 통합을 검증하는 방법을 설명합니다.
이러한 변경 사항은 대부분 오류 대신 잘못된 데이터나 누락된 데이터를 반환합니다. 따라서 예외 로그에 나타나지 않습니다.
활성화 일정
| 시점 | 내용 | 상태 |
| 2026년 8월 11일 | Anza가 메인넷에 4.2를 권장하고 검증인 업그레이드 시작 | 진행 중 |
| 노드 업그레이드 시 | 계정 업데이트 억제 및 Token-2022 jsonParsed 변경 사항 적용 | 완료 |
| 업그레이드 후 첫 번째 에포크 경계 | 보상에 DeactivatedStake rewardType 값 표시 | 완료 |
| 에포크 1020 | 슬롯 시간이 400ms에서 350ms로 단축, 200ms까지 총 4단계 중 첫 단계 | 완료 |
| 에포크 1024 | 슬롯 시간이 350ms에서 300ms로 단축 | 예정(8월 28일 예상) |
| 기능 게이트, 날짜 미정 | 트랜잭션 v1(4,096바이트 트랜잭션) | 대기 중(발표된 날짜가 없으므로 다음 주에 활성화된다고 가정하고 준비하세요) |
| 기능 게이트 5개, 날짜 미정 | 임대료 인하, 6,960 lamports/byte에서 696까지 단계적으로 인하 | 대기 중 |
| Agave 4.3, 2026년 10월 목표 | Alpenglow 합의 | 향후 릴리스 |
Agave 4.2 호환성 중단 변경 사항
트랜잭션 v1이 전체 호출을 실패시킵니다
SIMD-0296과 SIMD-0385는 새로운 트랜잭션 형식을 통해 Solana 트랜잭션 크기 제한을 4,096바이트로 늘립니다.
추가된 공간을 활용하면 ZK 증명이나 더 많은 경로를 거치는 DEX 라우팅처럼 더 큰 온체인 워크로드를 실행할 수 있습니다.
문제는 블록에 v1 트랜잭션이 하나라도 있을 때 호출에 maxSupportedTransactionVersion: 1을 설정하지 않으면 전체 블록의 getBlock가 실패한다는 점입니다.
다른 트랜잭션도 반환되지 않고 전체 호출에서 오류가 발생합니다. 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 크레이트.
- 모든 getBlock, getTransaction, getTransactionsForAddress 호출에
maxSupportedTransactionVersion: 1을 설정하세요(transactionDetails = full사용 시). - 모든 Enhanced WebSockets transactionSubscribe 구독의 옵션 객체에
maxSupportedTransactionVersion: 1을 설정하세요(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 트랜잭션은 컴퓨트 한도와 우선순위 수수료를 ComputeBudget 명령어가 아닌 transactionConfig 객체에 저장합니다. 해당 명령어를 매칭해 수수료를 감지하는 수수료 대시보드와 우선순위 수수료 추정기는 오류 없이 모든 v1 트랜잭션의 지불액을 0으로 읽습니다.
해결 방법:
대신 트랜잭션 구성의 새로운 priorityFee 필드에서 값을 읽으세요. 새로운 priorityFee 필드는 컴퓨트 단위당 가격이 아니라 lamports 단위의 총수수료를 나타냅니다.
"message": {
"instructions": ["… no ComputeBudget instruction here …"],
"recentBlockhash": "...",
"transactionConfig": {
"computeUnitLimit": 200000,
"heapSize": null,
"loadedAccountsDataSizeLimit": 200000,
"priorityFee": 50000
}
}
변경되지 않은 계정은 업데이트 전송을 중단합니다
Agave 4.2는 계정에 실제로 쓰기가 발생한 경우에만 계정 이벤트를 전송합니다. LaserStream gRPC 및 WSS 계정 구독에서는 이벤트가 약 80% 줄어듭니다.
해결 방법:
- 트랜잭션을 계정 업데이트와 매칭한다면 쓰기 가능한 모든 계정의 업데이트를 기다리지 마세요. 업데이트가 없으면 "계정이 변경되지 않음"으로 처리하세요. 수수료 납부자는 항상 수수료를 지불하므로 업데이트가 보장되는 유일한 계정입니다.
- 업데이트 빈도를 상태 신호로 사용한다면 자주 잠기지만 거의 변경되지 않는 계정에서는 해당 검사를 제거하세요. 이벤트가 없는 계정도 정상입니다. 아무것도 변경되지 않았기 때문에 이벤트 전송이 중단된 것입니다.
새로운 rewardType 값
비활성화가 완료된 스테이크 계정은 이제 getBlock 및 blockSubscribe 보상 배열에서 새로운 rewardType 값인 DeactivatedStake으로 최종 지급액을 받습니다. 보상 객체의 구조는 Agave 4.1과 동일합니다. 따라서 기존에 알고 있는 보상 유형만 허용하는 파서는 오류 없이 DeactivatedStake 지급액을 건너뜁니다.
getInflationReward은 영향을 받지 않습니다. 원시 보상 배열을 직접 파싱하는 경우에만 위험합니다.
해결 방법:
파서가 허용하는 rewardType 값에 DeactivatedStake을 추가하세요. 인식할 수 없는 값은 레코드를 삭제하지 말고 로그에 기록하세요.
{
"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"
}
}
}
권한이 필요한 소각, 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부터 350ms 슬롯으로 실행됩니다. 다음 단계인 300ms 단축은 에포크 1024에 예정되어 있으며 8월 28일로 예상됩니다. 최종 목표는 200ms입니다. 슬롯을 시간으로 변환하는 계산에 400ms를 하드코딩했다면 현재 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: 최신 Yellowstone protos와 solana-storage-proto에서 다시 생성하세요.
LaserStream SDK: JS 0.8.4, Rust 0.6.3, Go 0.2.0 이상에는 v1을 지원하는 proto가 포함되어 있습니다. 어떤 경로에서도 구독과 필터를 변경할 필요가 없습니다.
문의
4.2에서 통합이 다르게 작동하지만 이 글에서 원인을 찾을 수 없다면 Discord 또는 지원을 통해 문의하세요. 기능별 세부 내용은 4.2 개요를 확인하세요.
관련 아티클
Helius 구독하기
최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요


