· via dev.to (home feed)
Solana raises transaction limit to 4,096 bytes, but reading v1 transactions can break existing apps
Solana is lifting its 1,232-byte transaction cap to 4,096 bytes through a new v1 wire format, and code that only reads blocks — RPC clients, indexers, Geyser streams — can start failing or silently recording bad data.

A cap inherited from UDP
Solana's 1,232-byte transaction limit has stood since the chain launched, and according to a technical write-up on dev.to, it was never a design goal — it was plumbing. Transactions originally travelled as single UDP datagrams, so they had to fit the minimum IPv6 MTU of 1,280 bytes once 48 bytes of headers were subtracted. Solana's networking now runs on QUIC, which imposes no explicit stream size limit, so the physical reason for the cap is gone. Two proposals remove it: SIMD-0296 raises the ceiling to 4,096 bytes, and SIMD-0385 defines the v1 transaction format that carries it.
The article's author says every claim was checked against the proposal texts and exercised on a local Agave 4.2 validator. The 4,096 figure came from measuring real bundle traffic — roughly half of Jito bundles fit within 2,048 bytes and 65% within 6,144 — and from aligning with the 4 KiB memory pages validators already manage. Workloads that never fit the old envelope include zero-knowledge proofs for Confidential Transfers, Winternitz one-time signatures, nested institutional multisigs, and non-precompiled signature schemes such as BLS; teams previously worked around the cap with Jito bundles, which are not atomic at the protocol level. SIMD-0296 adds no per-byte fee — the expectation is that priority fees will price larger transactions.
A new wire format, not a stretched v0
SIMD-0385 does not enlarge the old envelope; it redesigns it. A v1 transaction announces itself with version byte 129 (0x81) and then diverges structurally. Resource requests — compute unit limit, loaded-accounts data size, heap size and priority fee — move out of ComputeBudget instructions into a fixed-position transactionConfig bitmask in the message. Address lookup tables are removed entirely: up to 64 full 32-byte accounts are inlined, with duplicates rejected during sanitization. Instruction headers become fixed-width and separated from their variable-length payloads, and signatures move to the tail with no length prefix. ComputeBudget instructions inside a v1 transaction still execute, but as no-ops that burn compute units and configure nothing.
Hard limits in v1: 4,096 bytes, 64 accounts, 64 instructions, 12 signatures, and 255 accounts per instruction. Two unit changes deserve attention. Priority fees switch from micro-lamports per compute unit in v0 to an absolute total in lamports in v1, so any pipeline that averages or compares fees across versions must normalise first. And v1 resource defaults are zero: legacy and v0 implicitly granted 200,000 compute units per instruction and 64 MiB of loaded-account data, while a v1 transaction that does not explicitly request a compute unit limit and loaded-accounts size fails at execution — after paying to land it. The @solana/kit library, from version 8.0.0, exposes setters that make each limit explicit.
Dropping lookup tables lands hardest on DeFi routing. About 62% of current v0 transactions reference at least one lookup table; converted to inline addresses, half grow by under 420 bytes and 90% by under 1,400 — comfortably inside the new envelope. Dense multi-table routes expand by 1,500+ bytes, however, and the 64-account cap does not move, so broad multi-pool aggregator strategies stay account-bound rather than byte-bound. A draft proposal, SIMD-0596, would raise the cap to 96.
Three ways reading breaks
Sending is opt-in: legacy and v0 remain valid indefinitely, and nothing changes for programs that never touch v1. Reading is a different story, and the article groups the fallout into three failure modes.
The first is loud. Once the feature gate activates, getTransaction, getBlock and blockSubscribe return JSON-RPC error -32015 for anything v1 unless the caller passes maxSupportedTransactionVersion: 1 — the integer, not the string "1". One v1 transaction anywhere in a block fails the entire getBlock response, not just that transaction. The article calls it a rerun of the 2022 v0 migration, and notes the parameter is backwards compatible and can ship before activation. Responses for v1 carry a transactionConfig object inside the message; v0 and legacy responses are unchanged.
The second is silent. Indexers commonly extract priority fees and compute limits by scanning for ComputeBudget instructions. Against a v1 transaction that scan finds nothing and returns zero, without an error — pipelines keep running while writing wrong fee and compute-unit data.
The third hides in streaming. Geyser and gRPC streams have no version gate at all, so v1 transactions simply arrive. Yellowstone builds before 15.1.1 silently downgrade v1 to v0 on the wire, and protobuf stubs generated before yellowstone-grpc-proto 12.6.0 have no Message.config field to decode. The fix: regenerate stubs, and detect the version structurally — check for the presence of config on the message before the versioned flag, since a v1 message also counts as versioned.
Why it matters
This is a breaking change for software that never sends a transaction. Block explorers, indexers, fee estimators and monitoring tools merely read chain data, and each v1 transaction can either crash their RPC calls or — worse — corrupt their datasets invisibly. An entire block response can fail over a single new-format transaction, and silently wrong fee data is harder to catch than an error. The practical path is to ship maxSupportedTransactionVersion: 1 now, teach indexers to read transactionConfig with a fallback to instruction scanning, normalise fee units across versions, and regenerate gRPC stubs before the feature gate activates. Once it does, being a read-only consumer of Solana data is no longer safe by default.
- #solana
- #blockchain
- #rpc
- #web3
- #transaction-format