0xMowgli.

/ Writing · · 7 min read

Solana v1 transactions: 4,096 bytes, no lookup tables, and a priority fee that moved

By Oussama Allamou · Solana · Transactions · Priority Fees · Trading Bots

A v1 transaction is Solana's third transaction format, live on mainnet since September 15, 2026 (epoch 1035). It can be 4,096 bytes where the limit was 1,232, it lists up to 64 accounts inline with no address lookup tables, and it sets the compute limit and priority fee in a config inside the message. Legacy and v0 transactions still work. Code that reads transactions has to learn the new format, because in a sample of mainnet blocks I took on October 12, 2026, v1 was already the largest share of non-vote traffic.

Everything here is from the Solana upgrade page and from calls I made to a public mainnet RPC node, shown below so you can repeat them. Start with the question every parser has to answer first.

TRANSACTIONraw bytes · JSON-RPC · gRPCSTARTS WITH 0x81?gRPC: message.config is setV1fee in the configMESSAGE 0x80?gRPC: versionedLEGACYstatic keys onlyV0lookup tablesYESNOYESNO
Explainer · Which version is this transaction?
  1. Step 01A transaction arrives

    You have raw bytes, a JSON-RPC response or a gRPC message. Before you read accounts or fees you need the format, because each one keeps them in a different place.

  2. Step 02It starts with 0x81: v1

    A v1 transaction puts its version byte first and its signatures last. JSON-RPC reports version 1. A gRPC message has no version field: it is v1 when message.config is set. Limits and the priority fee are in that config, and every account is listed inline.

  3. Step 03The message starts with 0x80: v0

    A v0 transaction starts with its signatures, then a message whose first byte is 0x80. gRPC sets versioned to true. Some accounts come from address lookup tables, and the fee comes from ComputeBudget instructions.

  4. Step 04Neither: legacy

    No version byte at all. Every account is a static key, and the fee comes from ComputeBudget instructions, as in v0. Votes are still legacy.

What is a Solana v1 transaction?

Legacyv0v1
Max bytes1,2321,2324,096
AccountsAbout 3264, via tables64, inline
Lookup tablesNoYesNo
DuplicatesAllowedAllowedRejected
Limits and fee set inInstructionsInstructionsConfig
RPC versionlegacy01

The size limit comes from SIMD-0296 and the format from SIMD-0385. The old 1,232 bytes was what fit in one network packet. The first byte of a v1 transaction is 0x81, so the format can be told apart before anything else is parsed, and the signatures moved to the end.

Bigger transactions are the point: more instructions and more accounts in one atomic step. It pairs with protocols making their own instructions smaller, as Pump did with its 17-account v3 trades.

How much of mainnet is already v1?

I read ten finalized blocks on October 12, 2026 and counted the version of every transaction.

Without votes

v1
43.8% · 2,179
v0
31.7% · 1,574
Legacy
24.5% · 1,217

With votes

v1
20.5% · 2,179
v0
14.8% · 1,574
Legacy
64.7% · 6,878

Legacy and v0 v1

Explainer · Transaction formats in ten mainnet blocks
  1. Case 01Without votes

    4,970 non-vote transactions: 2,179 v1, 1,574 v0 and 1,217 legacy. Ten blocks, every second slot from 456,186,169 to 456,186,187, read from a public RPC node. A small sample, so treat the split as a snapshot.

  2. Case 02With votes

    All 10,631 transactions in the same ten blocks. Validator votes are legacy transactions, which is why legacy dominates the raw count.

Four weeks after activation, ignoring v1 means ignoring a large part of real traffic.

How do I tell which version a transaction is?

  • Raw bytes. A v1 transaction starts with 0x81. A v0 transaction starts with its signatures, and its message starts with 0x80. A legacy message has no version byte.
  • JSON-RPC. The version field is "legacy", 0 or 1. A v1 message also has a transactionConfig object that the other two do not.
  • Yellowstone gRPC. There is no version field. A message with config set is v1. Without it, versioned true means v0, and neither means legacy. Check them in that order.

One trap on the gRPC side: the upgrade page says Yellowstone plugin builds before 15.1.1 downgrade v1 to v0, and yellowstone-grpc-proto 12.6.0 is the first version that has the config field. An old stack does not fail. It mislabels.

Where did the priority fee go?

In legacy and v0 you add two ComputeBudget instructions: a compute unit limit and a price per unit in micro-lamports. In v1 both live in the message config, and the fee is written as a total in lamports.

Legacy and v0 · two instructions

SetComputeUnitLimit(200,000)

SetComputeUnitPrice(50,000)

v1 · the message config

computeUnitLimit: 200,000

priorityFee: 10,000

Priority fee · lamports
10,000
Total with one signature
15,000 lamports
Try it · Priority fee, old and new
10,000 lamports

200,000 compute units at 50,000 micro-lamports each is 10,000 lamports. In v1 you write that total down. The price per unit is gone.

Multiply the limit by the price and divide by 1,000,000, rounding up. A parser that still looks for the two ComputeBudget instructions finds nothing in a v1 transaction and reports a priority fee of zero.

A real one from my sample: transaction 48y2cT8W…9FTwT in slot 456,186,169 has priorityFee: 627 in its config and was charged 5,627 lamports, which is the 5,000 lamport signature fee plus exactly that.

Three things to know when you send v1:

  1. Defaults are zero. In legacy and v0, leaving the limits out gave you 200,000 compute units per instruction. In v1 an unset compute limit is 0 and an unset loaded-accounts data size is 0 bytes, so a transaction with an empty config fails with MaxLoadedAccountsDataSizeExceeded.
  2. ComputeBudget instructions do nothing. They are not rejected. They are ignored, and each still costs 150 compute units and an instruction slot. Strip them.
  3. Fee comparisons need a conversion. To compare a v0 price with a v1 fee, multiply the price by the limit and divide by 1,000,000.

I described how the leader ranks transactions by fee in how a Solana transaction lands. That ranking did not change. Where you read the fee from did.

What breaks in RPC clients and indexers?

Ask for a v1 transaction without opting in and the node refuses. This is the real answer I got:

{"code":-32015,"message":"Transaction version (1) is not supported by the requesting client.
Please try the request again with the following configuration parameter:
\"maxSupportedTransactionVersion\": 1"}
  • getTransaction and getBlock need maxSupportedTransactionVersion: 1, as the number 1. With 0, getBlock with full transaction details failed for the whole block I tested, because one v1 transaction was in it.
  • blockSubscribe emits block: null for such blocks and stops advancing, according to the upgrade page. Treat a null block as a failure, not as an empty block.
  • Sending a transaction over 1,232 bytes needs encoding: "base64". base58 stays capped at the old size.
  • Fee analytics that scan for ComputeBudget instructions report zero for every v1 transaction. Read transactionConfig.
  • Account resolution is simpler: no lookup tables to load. But do not run your v0 lookup code on a v1 message.
  • RPC nodes need Agave 4.2.2 or later. Older builds store v1 as v0 with a zeroed compute budget.

What do I need to upgrade?

Minimum versions from the Solana upgrade page: @solana/kit 8.0.0 to read and send, @solana/web3.js 3.0.2 to read and send (1.99.0 reads only), Rust solana-* crates 4.2.x, solders 0.29.0 for Python, and yellowstone-grpc-proto 12.6.0 on the gRPC side. Wallets should advertise version 1 only once they can parse and sign it, and a dapp should fall back to v0 when the wallet does not.

Two other numbers changed under the same code this autumn: the slot went from 400 ms to 200 ms and the rent-exempt deposit was cut.

What do the terms mean?

  • Legacy transaction: the original format, with no version byte and only static account keys.
  • v0 transaction: the format that added address lookup tables, so a transaction can reference more accounts than fit in its bytes.
  • v1 transaction: the format with a 4,096 byte limit, inline accounts and a config for limits and fees.
  • Address lookup table: an on-chain list of addresses a v0 transaction points into by index.
  • Compute unit: the unit the runtime meters execution in. A transaction declares a limit.
  • Priority fee: what you pay on top of the 5,000 lamports per signature to rank higher in the leader's scheduler.

FAQ

What is a v1 transaction on Solana?

It is the transaction format activated on mainnet on September 15, 2026. It raises the maximum size from 1,232 to 4,096 bytes, lists up to 64 accounts inline without address lookup tables, and sets compute limits and the priority fee in a config inside the message.

Why does getTransaction return error -32015?

The transaction is a newer version than your request allows. Pass maxSupportedTransactionVersion set to 1 in the request config. The same applies to getBlock, which fails for the whole block if it contains one v1 transaction.

How do I set a priority fee on a v1 transaction?

Write it in the message config as a total in lamports, next to the compute unit limit. ComputeBudget instructions are ignored in v1. To convert an old setting, multiply the price in micro-lamports by the compute unit limit and divide by 1,000,000.

Do v1 transactions support address lookup tables?

No. Every account must be listed in the message, up to 64, and duplicate addresses are rejected. The larger size limit is what makes room for them.

Do I have to send v1 transactions?

No. Legacy and v0 transactions keep working. But anything that reads blocks or transactions has to accept v1, because other people's v1 transactions are in the same blocks.

Further reading