@solana/react
8.5.0 · 13 Oct 2026MIT10.1k / weekRepositoryHomepagenpmAtom feed
40 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.
- 8.x · latest line
8.5.0
13 Oct 2026[
@solana/addresses] #2115e59ed8bThanks @lorisleiva! - AllowgetAddressEncoder()andgetAddressCodec()to encode any address-bearing object (HasAddress) in addition to plainAddressvalues, so that signers and framework wrapper classes can be encoded directly without unwrapping them first. Decoders still return a plainAddress. As a result, the encoder input type of codecs composing an address codec (e.g. a struct with an address field) is now inferred asAddress | HasAddressinstead ofAddress.const addressEncoder = getAddressEncoder(); addressEncoder.encode(signer); // Same bytes as `addressEncoder.encode(signer.address)`.
[
@solana/codecs-data-structures,@solana/errors] #2089de1c63fThanks @latent-9! - Fix an infinite loop in thearray,set, andmapcodecs' sentinel and remainder decoding strategies: a zero-byte item codec (e.g.getStructCodec()) can never advance past a sentinel boundary or the end of the byte array, so decoding used to push elements until the process ran out of memory. Zero-byte fixed-size item codecs are now rejected on both the encoding and decoding sides when used with the sentinel or remainder strategies, throwing a new error instead:SOLANA_ERROR__CODECS__UNEXPECTED_ZERO_FIXED_SIZE_ITEM_FOR_ARRAY_LIKE_SIZE_STRATEGY. They remain allowed when the collection size is explicit.[
@solana/errors] #21110be380eThanks @mcintyre94! - Add theSOLANA_ERROR__WALLET__INVALID_MESSAGE_SIGNATUREandSOLANA_ERROR__WALLET__INVALID_SIGN_IN_OUTPUTerror codes, thrown when a wallet returns a message signature or a sign-in output that fails verification. Their context carries the signing account'saddressand thewalletName.Also add the
SOLANA_ERROR__WALLET__INVALID_NUMBER_OF_OUTPUTSerror code, thrown when a wallet feature returns a different number of outputs than the number of inputs it was given. Its context carries theexpectedandactualnumber of outputs, thefeatureNameand thewalletName.
@solana/kit
Minor Changes
[
@solana/addresses] #2115e59ed8bThanks @lorisleiva! - AllowgetAddressEncoder()andgetAddressCodec()to encode any address-bearing object (HasAddress) in addition to plainAddressvalues, so that signers and framework wrapper classes can be encoded directly without unwrapping them first. Decoders still return a plainAddress. As a result, the encoder input type of codecs composing an address codec (e.g. a struct with an address field) is now inferred asAddress | HasAddressinstead ofAddress.const addressEncoder = getAddressEncoder(); addressEncoder.encode(signer); // Same bytes as `addressEncoder.encode(signer.address)`.
[
@solana/codecs-data-structures,@solana/errors] #2089de1c63fThanks @latent-9! - Fix an infinite loop in thearray,set, andmapcodecs' sentinel and remainder decoding strategies: a zero-byte item codec (e.g.getStructCodec()) can never advance past a sentinel boundary or the end of the byte array, so decoding used to push elements until the process ran out of memory. Zero-byte fixed-size item codecs are now rejected on both the encoding and decoding sides when used with the sentinel or remainder strategies, throwing a new error instead:SOLANA_ERROR__CODECS__UNEXPECTED_ZERO_FIXED_SIZE_ITEM_FOR_ARRAY_LIKE_SIZE_STRATEGY. They remain allowed when the collection size is explicit.[
@solana/errors] #21110be380eThanks @mcintyre94! - Add theSOLANA_ERROR__WALLET__INVALID_MESSAGE_SIGNATUREandSOLANA_ERROR__WALLET__INVALID_SIGN_IN_OUTPUTerror codes, thrown when a wallet returns a message signature or a sign-in output that fails verification. Their context carries the signing account'saddressand thewalletName.Also add the
SOLANA_ERROR__WALLET__INVALID_NUMBER_OF_OUTPUTSerror code, thrown when a wallet feature returns a different number of outputs than the number of inputs it was given. Its context carries theexpectedandactualnumber of outputs, thefeatureNameand thewalletName.[
@solana/errors,@solana/keys,@solana/webcrypto-ed25519-polyfill] #2095cfe1d62Thanks @mcintyre94! -verifySignature()now verifies strictly, regardless of whether the underlying WebCrypto implementation does so itself (Node.js, for instance, does not). It returnsfalsewhen either the public key or theRcomponent of the signature is a point of small order. Previously, trivially forged signatures could verify against small-order public keys such as11111111111111111111111111111111.BREAKING CHANGES
verifySignature()throws unless given an extractable public key. Checking the public key requires reading its bytes, so a non-extractable public key now causes it to throw aSolanaErrorwith codeSOLANA_ERROR__KEYS__VERIFICATION_REQUIRES_EXTRACTABLE_PUBLIC_KEY. The same error is thrown for a key that is not a public key, which WebCrypto never accepted for verification anyway (it threw anInvalidAccessError). Every public key produced by Kit is extractable; if you import one yourself, setextractabletotrue.- const publicKey = await crypto.subtle.importKey('raw', bytes, 'Ed25519', false, ['verify']); + const publicKey = await crypto.subtle.importKey('raw', bytes, 'Ed25519', true, ['verify']); await verifySignature(publicKey, signature, data);getPublicKeyFromPrivateKey()always returns an extractable public key. Public keys are not secret, so this mirrors the behavior ofcrypto.subtle.generateKey(). Theextractableargument is deprecated and ignored.- const publicKey = await getPublicKeyFromPrivateKey(privateKey, true); + const publicKey = await getPublicKeyFromPrivateKey(privateKey);
The Ed25519 polyfill now also rejects public keys and signature
Rcomponents of small order, and opts out of@noble/ed25519's default ZIP-215 semantics.[
@solana/wallet-account-signer] #21129021e99Thanks @mcintyre94! -createMessageSignerFromWalletAccountnow verifies each signature returned by the wallet against the account's address and the message the wallet signed, andmodifyAndSignMessagesrejects withSOLANA_ERROR__WALLET__INVALID_MESSAGE_SIGNATUREif any of them is invalid. It also rejects withSOLANA_ERROR__WALLET__INVALID_NUMBER_OF_OUTPUTSif the wallet returns a different number of signed messages than it was asked to sign. The returned message content and signatures are copies of the wallet's output, so the wallet cannot change them after they are verified. Verifying signatures requires Ed25519 support in the WebCrypto API; in environments without it, install@solana/webcrypto-ed25519-polyfill.
Patch Changes
[
@solana/codecs-strings] #21177ea5132Thanks @Iamsohungrynow! -getBase16Encoderno longer drops the last digit of odd-length strings longer than one character:'abc'now encodes to[0xab, 0x0c]instead of[0xab, 0x00], andwriteadvances by the sizegetSizeFromValuereports.[
@solana/errors,@solana/sysvars,@solana/transaction-messages] #2110f47f025Thanks @mcintyre94! - Type-checkSolanaErrorcontexts in projects whose TypeScriptlibdoes not includeES2022.Error. TheSolanaErrorconstructor referenced the globalErrorOptionstype, which is unresolved without that lib, so withskipLibCheckenabled its context parameter silently becameanyand any context was accepted. Fixes the error contexts this was hiding:compileTransactionMessageanddecompileTransactionMessagenow report theunsupportedVersioninSOLANA_ERROR__TRANSACTION__VERSION_NUMBER_NOT_SUPPORTED, and theSlotHistorysysvar decoder now provides the expected context forSOLANA_ERROR__CODECS__INVALID_BYTE_LENGTHandSOLANA_ERROR__CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE, so their messages no longer contain unfilled placeholders.[
@solana/fixed-points] #2102d7a1d10Thanks @lorisleiva! - Allow binary fixed-points whosefractionalBitsexceed theirtotalBits, e.g. an unsigned 8-bit value with 12 fractional bits representing values in[0, 1/16), consistently with decimal fixed-points whosedecimalsmay already exceed theirtotalBits. TheSOLANA_ERROR__FIXED_POINTS__FRACTIONAL_BITS_EXCEED_TOTAL_BITSerror is no longer thrown.[
@solana/plugin-interfaces] #210441896b7Thanks @lorisleiva! - FixClientWithTransactionPlanning,ClientWithTransactionSendingandClientWithTransactionSigningrejecting themaxInstructionsPerTransactionoption that transaction planners support per call. Their planning, sending and signing functions now accept it, making it possible to override the maximum number of instructions per transaction for a single request. Sending and signing functions only use it when their input needs to be planned first.await client.planTransactions(instructions, { maxInstructionsPerTransaction: 8 }); await client.sendTransactions(instructions, { maxInstructionsPerTransaction: 8 }); await client.signTransactions(instructions, { maxInstructionsPerTransaction: 8 });[
@solana/rpc-graphql,@solana/rpc-parsed-types] #2101abfded6Thanks @ara-stock! - Fix the parsedepochRewardssysvar type to match what Agave returns. ThejsonParsedinfo hasactive,distributedRewards,distributionStartingBlockHeight,numPartitions,parentBlockhash,totalPointsandtotalRewards, and the reward amounts and points are strings. The old type described adistributionCompleteBlockHeightfield that Agave does not return, and typed the amounts asbigint.[
@solana/sysvars] #2116534ea5eThanks @Iamsohungrynow! - TheSysvarSlotHashesandSysvarRecentBlockhashesencoders/decoders now use theu64length prefix that these sysvar accounts actually have, instead of au32one. Previously every decoded entry was shifted by four bytes, so the slots, hashes and fees came back wrong.
8.4.0
28 Sep 2026[
@solana/codecs-data-structures,@solana/errors] #20610e76741Thanks @lorisleiva! - Add a sentinel size strategy to thearray,set, andmapcodecs. Passing a{ __kind: 'sentinel', sentinel, strategy? }object as thesizeoption ends the collection when the bytes at the next item position match the givensentinel, compared at item boundaries only. The optionalstrategy('required'by default, or'optional'/'omitted') controls whether the sentinel is written when encoding and required when decoding. This provides codec support for Codama'ssentinelCountNode.Two new errors accompany this:
SOLANA_ERROR__CODECS__SENTINEL_MISSING_AT_END_OF_BYTES(thrown under the'required'strategy when the byte array ends without the sentinel) andSOLANA_ERROR__CODECS__SENTINEL_MUST_NOT_BE_EMPTY(thrown when constructing a codec with an empty sentinel).[
@solana/errors,@solana/instruction-plans] #20735be269cThanks @lorisleiva! - Add helpers for writing custom message packers.resolveMaxInstructionsPerTransaction,assertMaxInstructionsPerTransactionandassertMessageCanAccommodateSizeenforce the instruction-count and size limits the built-in packers rely on, and a newSOLANA_ERROR__INSTRUCTION_PLANS__MESSAGE_REJECTED_BY_PACKERerror lets a packer refuse a transaction message for any other reason by providing areason. The transaction planner treats this error like the existing capacity errors and opens a new transaction message. UseisMessagePackerErrorThatRequiresNewCandidateto identify every error that calls for a new transaction message.[
@solana/codecs-numbers] #206756b4960Thanks @latent-9! - FixedgetShortU16Decoder()silently accepting malformed input: a truncated buffer decoded to garbage with an offset past the end of the byte array, and continuation chains longer than the documented three-byte encoding decoded to values outside the u16 domain. Malformed input now throwsSOLANA_ERROR__CODECS__INVALID_BYTE_LENGTH, and decoded values above 65,535 now throwSOLANA_ERROR__CODECS__NUMBER_OUT_OF_RANGE, matching the guards every other number decoder already uses.
@solana/kit
Minor Changes
[
@solana/codecs-data-structures,@solana/errors] #20610e76741Thanks @lorisleiva! - Add a sentinel size strategy to thearray,set, andmapcodecs. Passing a{ __kind: 'sentinel', sentinel, strategy? }object as thesizeoption ends the collection when the bytes at the next item position match the givensentinel, compared at item boundaries only. The optionalstrategy('required'by default, or'optional'/'omitted') controls whether the sentinel is written when encoding and required when decoding. This provides codec support for Codama'ssentinelCountNode.Two new errors accompany this:
SOLANA_ERROR__CODECS__SENTINEL_MISSING_AT_END_OF_BYTES(thrown under the'required'strategy when the byte array ends without the sentinel) andSOLANA_ERROR__CODECS__SENTINEL_MUST_NOT_BE_EMPTY(thrown when constructing a codec with an empty sentinel).[
@solana/errors,@solana/instruction-plans] #20735be269cThanks @lorisleiva! - Add helpers for writing custom message packers.resolveMaxInstructionsPerTransaction,assertMaxInstructionsPerTransactionandassertMessageCanAccommodateSizeenforce the instruction-count and size limits the built-in packers rely on, and a newSOLANA_ERROR__INSTRUCTION_PLANS__MESSAGE_REJECTED_BY_PACKERerror lets a packer refuse a transaction message for any other reason by providing areason. The transaction planner treats this error like the existing capacity errors and opens a new transaction message. UseisMessagePackerErrorThatRequiresNewCandidateto identify every error that calls for a new transaction message.
Patch Changes
[
@solana/codecs-numbers] #206756b4960Thanks @latent-9! - FixedgetShortU16Decoder()silently accepting malformed input: a truncated buffer decoded to garbage with an offset past the end of the byte array, and continuation chains longer than the documented three-byte encoding decoded to values outside the u16 domain. Malformed input now throwsSOLANA_ERROR__CODECS__INVALID_BYTE_LENGTH, and decoded values above 65,535 now throwSOLANA_ERROR__CODECS__NUMBER_OUT_OF_RANGE, matching the guards every other number decoder already uses.[
@solana/instruction-plans] #207175653e9Thanks @lorisleiva! - FixgetReallocMessagePackerInstructionPlanproducing a 0-byte instruction whentotalSizeis an exact multiple of the realloc limit (10,240 bytes), which left the account one chunk short of the requested size.
8.3.0
9 Sep 2026[
@solana/addresses,@solana/transaction-messages] #20257a14614Thanks @lorisleiva! - Add a newHasAddresstype representing any object exposing a Solana address through anaddressproperty — e.g. aTransactionSigner, anAccountMetaor a framework's address wrapper class. The fee payer of a transaction message is now typed usingHasAddress(a structurally identical change).[
@solana/codecs-core] #2030a5267b3Thanks @lorisleiva! - Addtapcodec helpers for observing values and bytes without modifying them. The value family (tapEncoder/tapDecoder/tapCodec) observes the input value before encoding and the decoded value after decoding, whilst the bytes family (tapEncoderBytes/tapDecoderBytes/tapCodecBytes) observes the raw bytes after encoding and before decoding. Any tap may throw to abort the operation, making these helpers ideal for adding validation guards to existing codecs without an identitytransformEncoder. For example,tapDecoderBytes(getBooleanDecoder(), (bytes, offset) => { if (bytes[offset] > 1) throw new Error('Expected a 0 or a 1 for booleans'); }).[
@solana/codecs-data-structures] #2042cd2776eThanks @lorisleiva! - Add arequireSizePrefixoption to the array, map and set codecs. By default, decoding an exhausted byte array yields an empty collection so that collections can be appended to existing data layouts; withrequireSizePrefix: true, a missing size prefix throws instead.
@solana/kit
Minor Changes
[
@solana/addresses,@solana/transaction-messages] #20257a14614Thanks @lorisleiva! - Add a newHasAddresstype representing any object exposing a Solana address through anaddressproperty — e.g. aTransactionSigner, anAccountMetaor a framework's address wrapper class. The fee payer of a transaction message is now typed usingHasAddress(a structurally identical change).[
@solana/codecs-core] #2030a5267b3Thanks @lorisleiva! - Addtapcodec helpers for observing values and bytes without modifying them. The value family (tapEncoder/tapDecoder/tapCodec) observes the input value before encoding and the decoded value after decoding, whilst the bytes family (tapEncoderBytes/tapDecoderBytes/tapCodecBytes) observes the raw bytes after encoding and before decoding. Any tap may throw to abort the operation, making these helpers ideal for adding validation guards to existing codecs without an identitytransformEncoder. For example,tapDecoderBytes(getBooleanDecoder(), (bytes, offset) => { if (bytes[offset] > 1) throw new Error('Expected a 0 or a 1 for booleans'); }).[
@solana/codecs-data-structures] #2042cd2776eThanks @lorisleiva! - Add arequireSizePrefixoption to the array, map and set codecs. By default, decoding an exhausted byte array yields an empty collection so that collections can be appended to existing data layouts; withrequireSizePrefix: true, a missing size prefix throws instead.[
@solana/codecs-numbers] #20293206678Thanks @lorisleiva! - Addu256andi256number codecs (getU256Codec/getU256Encoder/getU256DecoderandgetI256Codec/getI256Encoder/getI256Decoder), extending the number codecs beyond 128-bit integers. Both support little- and big-endian serialization via theendianoption and, as with the other large-integer codecs, always decode to abigint.[
@solana/codecs-strings,@solana/errors] #2041cae725cThanks @lorisleiva! - Addfatal,ignoreBOMandremoveNullCharactersoptions to the UTF-8 codec. Withfatal, lone surrogates when encoding and malformed byte sequences when decoding throw aSolanaErrorinstead of being replaced withU+FFFD. WithignoreBOM: true, a leading byte order mark is preserved instead of being stripped. WithremoveNullCharacters: false, null characters are preserved in decoded strings instead of being stripped as padding. On React Native, a leading byte order mark is now stripped by default, consistent with other platforms.[
@solana/errors,@solana/program-client-core] #20257a14614Thanks @lorisleiva! - Widen the accepted inputs of instruction accounts in generated program clients. The newInstructionAccountInputtype accepts anAddress, any address-bearing object (HasAddress) — including framework wrapper classes — aProgramDerivedAddressor anAccountNonSignerMetaused to override the role declared by the program's IDL. Similarly, the newInstructionSignerInputtype accepts aTransactionSigneror anAccountSignerMetarole override. In addition,ResolvedInstructionAccountnow carries an optionalisSignerflag describing the IDL's signer requirement: when set tofalse,TransactionSignervalues act as plain address carriers instead of being upgraded to signers, and when set totrue, a missing signer throws a helpful error pointing atcreateNoopSigner. Finally, newResolvedInstructionAccountMetaandInstructionAccountInputAddresstype helpers mirror this runtime logic at the type level so that generated instruction builders can accurately type the account metas they return.[
@solana/instructions] #20257a14614Thanks @lorisleiva! - Add a newAccountNonSignerMetatype representing anAccountMetawhose role is guaranteed not to be a signer role — i.e.ReadonlyAccount | WritableAccount. It is the counterpart of theAccountSignerMetatype from@solana/signers. Additionally, therolemember ofWritableAccount,ReadonlySignerAccountandWritableSignerAccountis now markedreadonly, consistently withReadonlyAccountandAccountMeta. Note that code mutating theroleof these types will now fail to compile — which was already contrary toAccountMeta's contract and would throw at runtime on frozen account metas.[
@solana/rpc-api,@solana/rpc-transport-http] #20161dd83c6Thanks @mcintyre94! - Added support for thegetAgGenesisCertRPC method, which returns the Alpenglow genesis certificate from nodes that have one[
@solana/signers] #20318af3229Thanks @lorisleiva! - AddcreateLazyKeyPairSignerFromBytes, a synchronous counterpart tocreateKeyPairSignerFromBytes. It derives the signer's address directly from the public key half of the 64-byte secret key and defers the asynchronousCryptoKeyimport until the first message or transaction is signed (memoising the result). This is useful when a signer must be created in a synchronous context whilst signing can remain asynchronous. Because the key import is deferred, the returned signer implements bothMessagePartialSignerandTransactionPartialSignerbut does not expose akeyPairproperty, and the cryptographic validation of the secret key happens on the first signing attempt rather than at creation time. The internal copy of the secret key is zeroed once the import succeeds, and a failed import is not cached so signing can be retried.
Patch Changes
[
@solana/rpc-transport-http] #20161dd83c6Thanks @mcintyre94! - Fixed a bug where responses togetTransactionsForAddressrequests were parsed withoutbigintsupport, risking precision loss on large integer values
8.2.0
29 Aug 2026- breaking
[
@solana/instruction-plans] #201326edd3bThanks @mcintyre94! - AligncreateTransactionPlanExecutorWithConcurrentLeaveswithcreateTransactionPlanExecutorby giving both the sameTransactionPlanExecutorConfig. ItsexecuteTransactionMessagecallback now receives a mutable per-leafcontextobject and returns the context of a successful result, instead of building a wholeSingleTransactionPlanResultitself. The executor builds the results: on success it merges the returned context over the stored one, and when the callback throws — or the abort signal fires before it settles — it preserves the stored context on the failed result, matching the partial-context behaviour ofcreateTransactionPlanExecutor. TheConcurrentLeafTransactionPlanExecutorConfigtype is removed. This is a breaking change to thecreateTransactionPlanExecutorWithConcurrentLeavesAPI introduced in 8.1.0. [
@solana/signers] #2006d385ce3Thanks @amilz! - Relaxed the parameter type of the signer type guards (isMessagePartialSigner,isTransactionSigner,isKeyPairSigner, their siblings, and theassertIs*variants) so that class instances implementing a signer interface can be passed without casts. The guards now accept anyTValue extends { address: Address }and narrow toTValue & Signer, whereas the previous parameter type required an implicit index signature, which TypeScript never grants to class instances.
8.1.0
27 Aug 2026[
@solana/errors,@solana/transaction-messages] #19727d56e29Thanks @skyyycodes! - Validate compute unit limit and heap size when they are set on a transaction messagesetTransactionMessageComputeUnitLimit,setTransactionMessageHeapSizeandsetTransactionMessageConfignow reject values the runtime will not honor as written, throwingSOLANA_ERROR__TRANSACTION__COMPUTE_UNIT_LIMIT_OUT_OF_RANGEorSOLANA_ERROR__TRANSACTION__INVALID_HEAP_SIZEat the point the value is set. An invalid heap size fails the transaction on-chain; a compute unit limit above the maximum is instead clamped down by the runtime, so the transaction quietly runs with a budget other than the one requested. A compute unit limit must be an integer in the range [0, 1,400,000]; a heap size must be an integer multiple of 1 KiB between 32 KiB and 256 KiB inclusive.Decoding is unaffected:
decompileTransactionMessagestill returns messages carrying out-of-range values so that callers can inspect any transaction that reaches them.[
@solana/instruction-plans] #199595a2788Thanks @mcintyre94! - AddcreateTransactionPlanExecutorWithConcurrentLeavesto concurrently transform every transaction plan leaf while preserving the plan's result shape.[
@solana/codecs-core] #1970be30e32Thanks @koriyoshi2041! - FixtoArrayBufferslicing SharedArrayBuffer-backed views from a non-zero byte offset.
@solana/kit
Minor Changes
[
@solana/errors,@solana/transaction-messages] #19727d56e29Thanks @skyyycodes! - Validate compute unit limit and heap size when they are set on a transaction messagesetTransactionMessageComputeUnitLimit,setTransactionMessageHeapSizeandsetTransactionMessageConfignow reject values the runtime will not honor as written, throwingSOLANA_ERROR__TRANSACTION__COMPUTE_UNIT_LIMIT_OUT_OF_RANGEorSOLANA_ERROR__TRANSACTION__INVALID_HEAP_SIZEat the point the value is set. An invalid heap size fails the transaction on-chain; a compute unit limit above the maximum is instead clamped down by the runtime, so the transaction quietly runs with a budget other than the one requested. A compute unit limit must be an integer in the range [0, 1,400,000]; a heap size must be an integer multiple of 1 KiB between 32 KiB and 256 KiB inclusive.Decoding is unaffected:
decompileTransactionMessagestill returns messages carrying out-of-range values so that callers can inspect any transaction that reaches them.[
@solana/instruction-plans] #199595a2788Thanks @mcintyre94! - AddcreateTransactionPlanExecutorWithConcurrentLeavesto concurrently transform every transaction plan leaf while preserving the plan's result shape.
Patch Changes
[
@solana/codecs-core] #1970be30e32Thanks @koriyoshi2041! - FixtoArrayBufferslicing SharedArrayBuffer-backed views from a non-zero byte offset.[
@solana/codecs-data-structures] #1971c51b9f6Thanks @shin4141! - Preserve the literalfixedSizetype for single-field fixed-size struct codecs.[
@solana/codecs-strings] #19796852054Thanks @amilz! - FixedgetBase64Decoder()throwingRangeError: Maximum call stack size exceededin browser builds when decoding byte arrays larger than roughly 65KB. The bytes are now converted to a binary string in chunks rather than spread into a singleString.fromCharCodecall.[
@solana/rpc,@solana/transaction-confirmation] #1994490ac9eThanks @matusbalascak! - Fix transaction confirmation and coalesced RPC request cancellation in environments where abort events have a null target.
8.0.0
21 Aug 2026- breaking
[
@solana/instruction-plans] #191380368ebThanks @mcintyre94! - Stop writing to the execution context increateTransactionPlanExecutorThe
executeTransactionMessagecallback can no longer return aSignatureor aTransaction. Those return values were deprecated when the callback gained the ability to return the context that a successful result should carry, and they are now gone: that context, a completeTContext, is the only thing the callback returns. Nothing is written to it on your behalf.const transactionPlanExecutor = createTransactionPlanExecutor({ executeTransactionMessage: async (context, message) => { const transaction = await signTransactionMessageWithSigners(message); context.transaction = transaction; + const signature = getSignatureFromTransaction(transaction); await sendAndConfirmTransaction(transaction, { commitment: 'confirmed' }); - return transaction; + return { signature, transaction }; }, });The mutable
contextargument is still there, and still serves the failure path: whatever the callback stores on it before it throws is preserved in the resultingFailedSingleTransactionPlanResult. The two channels differ only in which outcome they feed. Mutating the context makes a value available to a failed result; returning it makes a value available to a successful one. On success the two are merged, with the returned value taking precedence, so a property stored but not returned is still reported.Note that the callback cannot simply return the context it was given — every property on it is optional, so it does not satisfy
TContext. Build the return value from the values you have instead. This is the point of the return type: a callback that declares a context with a requiredsignatureand never produces one now fails to compile, rather than yielding a successful result whosecontext.signatureis typed butundefinedat runtime.This unblocks executors that never obtain a fee payer signature. Previously the executor derived
context.signatureby callinggetSignatureFromTransactionon a returned transaction, and on any transaction found on the context while handling a failure. That call throwsSOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSINGwhen the fee payer slot is empty, so an executor that deliberately produces partially signed transactions — signed by an authority, to be paid for and submitted by a relayer later — could not succeed, and one that stored such a transaction before failing had its original error replaced by that one. Neither derivation exists any more, so both cases now work. Declare a context type that does not require a signature and store just the transaction:const transactionPlanExecutor = createTransactionPlanExecutor<{ transaction: Transaction }>({ executeTransactionMessage: async (_context, message) => { return { transaction: await signTransactionMessageWithSigners(message) }; }, });Signatures are no longer added behind your back. An executor whose
TContextrequires asignature— including the defaultTransactionPlanResultContextWithSignature— must now produce one itself, and the compiler holds it to that. Failed results carry only what the callback stored on the context before it threw; asignatureis no longer recovered from a stored transaction.BaseTransactionPlanResultContextis removed. It described the fields the executor used to write on your behalf, and nothing writes them any more — what a context holds is entirelyTContext's business. UseTransactionPlanResultContextWithSignaturewhere you want the signature guarantee, or declare the optionalmessage/signature/transactionfields your own context actually needs.successfulSingleTransactionPlanResultFromTransactionis removed. It was the last place that derived asignatureon your behalf — by callinggetSignatureFromTransaction, with the same fee-payer-signature requirement described above — and the executor no longer uses it. Construct results withsuccessfulSingleTransactionPlanResultinstead, passing the context explicitly:- successfulSingleTransactionPlanResultFromTransaction(message, transaction); + successfulSingleTransactionPlanResult(message, { + signature: getSignatureFromTransaction(transaction), + transaction, + }); - breaking
[
@solana/instruction-plans,@solana/kit,@solana/rpc-transformers,@solana/transactions] #194834568a9Thanks @mcintyre94! - Remove APIs that were deprecated in previous versions: the compute-unit-limit estimation helpers in@solana/kit, thegetBigIntDowncastRequestTransformerin@solana/rpc-transformers, the fixed transaction size constants in@solana/transactions, and theSuccessfulBaseTransactionPlanResultContexttype in@solana/instruction-plans.BREAKING CHANGES
estimateComputeUnitLimitFactoryremoved from@solana/kit. UseestimateResourceLimitsFactoryinstead. The resource-limits estimator returns both the compute unit limit and (for version 1 transactions) the loaded accounts data size limit from a single simulation call.- const estimateComputeUnitLimit = estimateComputeUnitLimitFactory({ rpc }); - const computeUnitLimit = await estimateComputeUnitLimit(transactionMessage); + const estimateResourceLimits = estimateResourceLimitsFactory({ rpc }); + const { computeUnitLimit } = await estimateResourceLimits(transactionMessage);estimateAndSetComputeUnitLimitFactoryremoved from@solana/kit. UseestimateAndSetResourceLimitsFactoryinstead, which additionally sets the loaded accounts data size limit for version 1 transactions.- const estimateAndSet = estimateAndSetComputeUnitLimitFactory(estimateComputeUnitLimitFactory({ rpc })); + const estimateAndSet = estimateAndSetResourceLimitsFactory(estimateResourceLimitsFactory({ rpc })); const updatedMessage = await estimateAndSet(transactionMessage);fillTransactionMessageProvisoryComputeUnitLimitremoved from@solana/kit. UsefillTransactionMessageProvisoryResourceLimitsinstead, which additionally reserves space for the loaded accounts data size limit on version 1 transactions.- const filledMessage = fillTransactionMessageProvisoryComputeUnitLimit(transactionMessage); + const filledMessage = fillTransactionMessageProvisoryResourceLimits(transactionMessage);
getBigIntDowncastRequestTransformerremoved from@solana/rpc-transformers. This transformer was no longer used by the default Solana RPC request transformer. The Solana RPC transport serializesbigintvalues losslessly as large integer literals, and Agave parses JSON integers across the fullu64range without precision loss, so downcastingbigints to (potentially lossy)numbers is unnecessary. If you still need this behavior, recreate it withgetTreeWalkerRequestTransformer.TRANSACTION_PACKET_SIZE,TRANSACTION_PACKET_HEADER, andTRANSACTION_SIZE_LIMITremoved from@solana/transactions. Transaction size is no longer constant, as version 1 transactions have a larger size limit. UsegetTransactionSizeLimitto get the size limit for a specific transaction based on its version, or theLEGACY_TRANSACTION_SIZE_LIMITandV1_TRANSACTION_SIZE_LIMITconstants for a specific version.- const numFreeBytes = TRANSACTION_SIZE_LIMIT - getTransactionSize(transaction); + const numFreeBytes = getTransactionSizeLimit(transaction) - getTransactionSize(transaction);
SuccessfulBaseTransactionPlanResultContextremoved from@solana/instruction-plans. UseTransactionPlanResultContextWithSignatureinstead as the context type argument.- function processResult(result: SuccessfulSingleTransactionPlanResult<SuccessfulBaseTransactionPlanResultContext>) { + function processResult(result: SuccessfulSingleTransactionPlanResult<TransactionPlanResultContextWithSignature>) { - breaking
[
@solana/instruction-plans] #19107b983baThanks @mcintyre94! - LetTContextdecide what a transaction plan result context containsThe context attached to a transaction plan result was never entirely controlled by the executor.
SuccessfulSingleTransactionPlanResulthardcoded it asSuccessfulBaseTransactionPlanResultContext & TContext, andcreateTransactionPlanExecutormixed further properties into both the callback's context and the executor's result type. Because intersections only ever narrow, no choice ofTContextcould relax the requiredsignature— which made it impossible to type an executor that partially signs transactions for a relayer to submit later.TContextis now the only thing that says what a context contains. The signature guarantee moved out of the structure of the result types and into the default value ofTContext, so every zero-type-argument spelling behaves exactly as it did before.A new context type.
TransactionPlanResultContextWithSignatureguarantees asignatureand is the new default everywhere.SuccessfulBaseTransactionPlanResultContextis deprecated in favour of it.Explicit context types must be migrated. Intersect the new default to keep the signature guarantee:
- SingleTransactionPlanResult<{ startedAt: number }> + SingleTransactionPlanResult<TransactionPlanResultContextWithSignature & { startedAt: number }>The executor callback now receives
Partial<TContext>. A fresh, empty context is created for every transaction message and filling it in is the callback's job, but its properties used to be typed as required — so a callback could read one before writing it, be told a value was there, and getundefinedat runtime. Everything is optional on entry now. Writing still narrows, so a read aftercontext.custom = 'value'gives the non-optional type.One consequence is that a callback can no longer annotate its own parameter with required properties, which was a common way to infer a custom context. Use a type argument instead:
- createTransactionPlanExecutor({ - executeTransactionMessage: async (context: { startedAt: number }) => { /* ... */ }, - }) + createTransactionPlanExecutor<TransactionPlanResultContextWithSignature & { startedAt: number }>({ + executeTransactionMessage: async context => { /* ... */ }, + })A returned context no longer has to carry a
signature. TheexecuteTransactionMessagecallback may return the context that a successful result should carry, and that context used to need a signature because the result type hardcoded one.TContextdecides now: the default still requires it, and a customTContextthat omits it is accepted — which is what makes the relayer case above expressible end to end, rather than merely possible at runtime. Consequently the executor no longer tells a returned context apart from a returnedTransactionby looking for asignatureon it, since a context may not have one; it looks for thesignaturesmap that only aTransactionhas.Failed and canceled results type their context as
Readonly<Partial<TContext>>. Those branches never guaranteed custom properties at runtime — the context is built incrementally and the callback may throw at any point — so the types now say so.Inference from object literals is narrower, since the result constructors no longer add properties you did not pass.
successfulSingleTransactionPlanResult(message, { signature })infersReadonly<{ signature: Signature }>rather than a context that also carried optionalmessageandtransactionfields plus an index signature, so readingresult.context.messageoff it is now an error. On the failed and canceled constructors thePartialgoes further: even a field you did pass comes back optional, sofailedSingleTransactionPlanResult(message, error, { transaction }).context.transactionisTransaction | undefined. Pass an explicit type argument wherever you need a field to type as required. Relatedly,successfulSingleTransactionPlanResultFromTransaction's optionalcontextparameter changes fromOmit<BaseTransactionPlanResultContext, 'signature' | 'transaction'> & TContexttoTContext;signatureandtransactionare still derived from thetransactionargument and intersected into the result's context type.Helpers that consume results now accept any context.
passthroughFailedTransactionPlanExecution,createFailedToSendTransactionError,createFailedToSendTransactionsErrorandcreateFailedToExecuteTransactionPlanErrorwere non-generic, so they implicitly demanded the default signature-guaranteeing context and rejected results parameterised with anything else. They are now generic overTContextand preserve it. Where they read a signature to build an error message they narrow it at runtime, so a result without one simply omits it from the message.Execution behaviour is unchanged. The executor still populates
context.signaturefrom the signature or transaction your callback returns, and still recovers one for failed results from atransactionleft on the context. Those writes just no longer show up in the result type unless yourTContextasked for them. The behavioural changes are the two described above: an error message built from a context whosesignatureis absent, or is not a string, now omits it rather than interpolating whatever was there, and a returned context is recognised by the absence of asignaturesmap rather than the presence of asignature.
@solana/kit
Major Changes
- breaking
[
@solana/instruction-plans] #191380368ebThanks @mcintyre94! - Stop writing to the execution context increateTransactionPlanExecutorThe
executeTransactionMessagecallback can no longer return aSignatureor aTransaction. Those return values were deprecated when the callback gained the ability to return the context that a successful result should carry, and they are now gone: that context, a completeTContext, is the only thing the callback returns. Nothing is written to it on your behalf.const transactionPlanExecutor = createTransactionPlanExecutor({ executeTransactionMessage: async (context, message) => { const transaction = await signTransactionMessageWithSigners(message); context.transaction = transaction; + const signature = getSignatureFromTransaction(transaction); await sendAndConfirmTransaction(transaction, { commitment: 'confirmed' }); - return transaction; + return { signature, transaction }; }, });The mutable
contextargument is still there, and still serves the failure path: whatever the callback stores on it before it throws is preserved in the resultingFailedSingleTransactionPlanResult. The two channels differ only in which outcome they feed. Mutating the context makes a value available to a failed result; returning it makes a value available to a successful one. On success the two are merged, with the returned value taking precedence, so a property stored but not returned is still reported.Note that the callback cannot simply return the context it was given — every property on it is optional, so it does not satisfy
TContext. Build the return value from the values you have instead. This is the point of the return type: a callback that declares a context with a requiredsignatureand never produces one now fails to compile, rather than yielding a successful result whosecontext.signatureis typed butundefinedat runtime.This unblocks executors that never obtain a fee payer signature. Previously the executor derived
context.signatureby callinggetSignatureFromTransactionon a returned transaction, and on any transaction found on the context while handling a failure. That call throwsSOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSINGwhen the fee payer slot is empty, so an executor that deliberately produces partially signed transactions — signed by an authority, to be paid for and submitted by a relayer later — could not succeed, and one that stored such a transaction before failing had its original error replaced by that one. Neither derivation exists any more, so both cases now work. Declare a context type that does not require a signature and store just the transaction:const transactionPlanExecutor = createTransactionPlanExecutor<{ transaction: Transaction }>({ executeTransactionMessage: async (_context, message) => { return { transaction: await signTransactionMessageWithSigners(message) }; }, });Signatures are no longer added behind your back. An executor whose
TContextrequires asignature— including the defaultTransactionPlanResultContextWithSignature— must now produce one itself, and the compiler holds it to that. Failed results carry only what the callback stored on the context before it threw; asignatureis no longer recovered from a stored transaction.BaseTransactionPlanResultContextis removed. It described the fields the executor used to write on your behalf, and nothing writes them any more — what a context holds is entirelyTContext's business. UseTransactionPlanResultContextWithSignaturewhere you want the signature guarantee, or declare the optionalmessage/signature/transactionfields your own context actually needs.successfulSingleTransactionPlanResultFromTransactionis removed. It was the last place that derived asignatureon your behalf — by callinggetSignatureFromTransaction, with the same fee-payer-signature requirement described above — and the executor no longer uses it. Construct results withsuccessfulSingleTransactionPlanResultinstead, passing the context explicitly:- successfulSingleTransactionPlanResultFromTransaction(message, transaction); + successfulSingleTransactionPlanResult(message, { + signature: getSignatureFromTransaction(transaction), + transaction, + }); - breaking
[
@solana/instruction-plans,@solana/kit,@solana/rpc-transformers,@solana/transactions] #194834568a9Thanks @mcintyre94! - Remove APIs that were deprecated in previous versions: the compute-unit-limit estimation helpers in@solana/kit, thegetBigIntDowncastRequestTransformerin@solana/rpc-transformers, the fixed transaction size constants in@solana/transactions, and theSuccessfulBaseTransactionPlanResultContexttype in@solana/instruction-plans.BREAKING CHANGES
estimateComputeUnitLimitFactoryremoved from@solana/kit. UseestimateResourceLimitsFactoryinstead. The resource-limits estimator returns both the compute unit limit and (for version 1 transactions) the loaded accounts data size limit from a single simulation call.- const estimateComputeUnitLimit = estimateComputeUnitLimitFactory({ rpc }); - const computeUnitLimit = await estimateComputeUnitLimit(transactionMessage); + const estimateResourceLimits = estimateResourceLimitsFactory({ rpc }); + const { computeUnitLimit } = await estimateResourceLimits(transactionMessage);estimateAndSetComputeUnitLimitFactoryremoved from@solana/kit. UseestimateAndSetResourceLimitsFactoryinstead, which additionally sets the loaded accounts data size limit for version 1 transactions.- const estimateAndSet = estimateAndSetComputeUnitLimitFactory(estimateComputeUnitLimitFactory({ rpc })); + const estimateAndSet = estimateAndSetResourceLimitsFactory(estimateResourceLimitsFactory({ rpc })); const updatedMessage = await estimateAndSet(transactionMessage);fillTransactionMessageProvisoryComputeUnitLimitremoved from@solana/kit. UsefillTransactionMessageProvisoryResourceLimitsinstead, which additionally reserves space for the loaded accounts data size limit on version 1 transactions.- const filledMessage = fillTransactionMessageProvisoryComputeUnitLimit(transactionMessage); + const filledMessage = fillTransactionMessageProvisoryResourceLimits(transactionMessage);
getBigIntDowncastRequestTransformerremoved from@solana/rpc-transformers. This transformer was no longer used by the default Solana RPC request transformer. The Solana RPC transport serializesbigintvalues losslessly as large integer literals, and Agave parses JSON integers across the fullu64range without precision loss, so downcastingbigints to (potentially lossy)numbers is unnecessary. If you still need this behavior, recreate it withgetTreeWalkerRequestTransformer.TRANSACTION_PACKET_SIZE,TRANSACTION_PACKET_HEADER, andTRANSACTION_SIZE_LIMITremoved from@solana/transactions. Transaction size is no longer constant, as version 1 transactions have a larger size limit. UsegetTransactionSizeLimitto get the size limit for a specific transaction based on its version, or theLEGACY_TRANSACTION_SIZE_LIMITandV1_TRANSACTION_SIZE_LIMITconstants for a specific version.- const numFreeBytes = TRANSACTION_SIZE_LIMIT - getTransactionSize(transaction); + const numFreeBytes = getTransactionSizeLimit(transaction) - getTransactionSize(transaction);
SuccessfulBaseTransactionPlanResultContextremoved from@solana/instruction-plans. UseTransactionPlanResultContextWithSignatureinstead as the context type argument.- function processResult(result: SuccessfulSingleTransactionPlanResult<SuccessfulBaseTransactionPlanResultContext>) { + function processResult(result: SuccessfulSingleTransactionPlanResult<TransactionPlanResultContextWithSignature>) { - breaking
[
@solana/instruction-plans] #19107b983baThanks @mcintyre94! - LetTContextdecide what a transaction plan result context containsThe context attached to a transaction plan result was never entirely controlled by the executor.
SuccessfulSingleTransactionPlanResulthardcoded it asSuccessfulBaseTransactionPlanResultContext & TContext, andcreateTransactionPlanExecutormixed further properties into both the callback's context and the executor's result type. Because intersections only ever narrow, no choice ofTContextcould relax the requiredsignature— which made it impossible to type an executor that partially signs transactions for a relayer to submit later.TContextis now the only thing that says what a context contains. The signature guarantee moved out of the structure of the result types and into the default value ofTContext, so every zero-type-argument spelling behaves exactly as it did before.A new context type.
TransactionPlanResultContextWithSignatureguarantees asignatureand is the new default everywhere.SuccessfulBaseTransactionPlanResultContextis deprecated in favour of it.Explicit context types must be migrated. Intersect the new default to keep the signature guarantee:
- SingleTransactionPlanResult<{ startedAt: number }> + SingleTransactionPlanResult<TransactionPlanResultContextWithSignature & { startedAt: number }>The executor callback now receives
Partial<TContext>. A fresh, empty context is created for every transaction message and filling it in is the callback's job, but its properties used to be typed as required — so a callback could read one before writing it, be told a value was there, and getundefinedat runtime. Everything is optional on entry now. Writing still narrows, so a read aftercontext.custom = 'value'gives the non-optional type.One consequence is that a callback can no longer annotate its own parameter with required properties, which was a common way to infer a custom context. Use a type argument instead:
- createTransactionPlanExecutor({ - executeTransactionMessage: async (context: { startedAt: number }) => { /* ... */ }, - }) + createTransactionPlanExecutor<TransactionPlanResultContextWithSignature & { startedAt: number }>({ + executeTransactionMessage: async context => { /* ... */ }, + })A returned context no longer has to carry a
signature. TheexecuteTransactionMessagecallback may return the context that a successful result should carry, and that context used to need a signature because the result type hardcoded one.TContextdecides now: the default still requires it, and a customTContextthat omits it is accepted — which is what makes the relayer case above expressible end to end, rather than merely possible at runtime. Consequently the executor no longer tells a returned context apart from a returnedTransactionby looking for asignatureon it, since a context may not have one; it looks for thesignaturesmap that only aTransactionhas.Failed and canceled results type their context as
Readonly<Partial<TContext>>. Those branches never guaranteed custom properties at runtime — the context is built incrementally and the callback may throw at any point — so the types now say so.Inference from object literals is narrower, since the result constructors no longer add properties you did not pass.
successfulSingleTransactionPlanResult(message, { signature })infersReadonly<{ signature: Signature }>rather than a context that also carried optionalmessageandtransactionfields plus an index signature, so readingresult.context.messageoff it is now an error. On the failed and canceled constructors thePartialgoes further: even a field you did pass comes back optional, sofailedSingleTransactionPlanResult(message, error, { transaction }).context.transactionisTransaction | undefined. Pass an explicit type argument wherever you need a field to type as required. Relatedly,successfulSingleTransactionPlanResultFromTransaction's optionalcontextparameter changes fromOmit<BaseTransactionPlanResultContext, 'signature' | 'transaction'> & TContexttoTContext;signatureandtransactionare still derived from thetransactionargument and intersected into the result's context type.Helpers that consume results now accept any context.
passthroughFailedTransactionPlanExecution,createFailedToSendTransactionError,createFailedToSendTransactionsErrorandcreateFailedToExecuteTransactionPlanErrorwere non-generic, so they implicitly demanded the default signature-guaranteeing context and rejected results parameterised with anything else. They are now generic overTContextand preserve it. Where they read a signature to build an error message they narrow it at runtime, so a result without one simply omits it from the message.Execution behaviour is unchanged. The executor still populates
context.signaturefrom the signature or transaction your callback returns, and still recovers one for failed results from atransactionleft on the context. Those writes just no longer show up in the result type unless yourTContextasked for them. The behavioural changes are the two described above: an error message built from a context whosesignatureis absent, or is not a string, now omits it rather than interpolating whatever was there, and a returned context is recognised by the absence of asignaturesmap rather than the presence of asignature.
Minor Changes
[
@solana/errors,@solana/instruction-plans] #1902cb09af6Thanks @mcintyre94! - AddSOLANA_ERROR__FAILED_TO_SIGN_TRANSACTIONandSOLANA_ERROR__FAILED_TO_SIGN_TRANSACTIONSerror codes, together with thecreateFailedToSignTransactionErrorandcreateFailedToSignTransactionsErrorfactories that raise them. These are the signing counterparts to the existing failed-to-send codes and factories, intended for high-level wrappers that sign transactions without submitting them: such a wrapper can now translate the low-levelSOLANA_ERROR__INSTRUCTION_PLANS__FAILED_TO_EXECUTE_TRANSACTION_PLANthrown by an executor into a user-facing error, exactly as the sending wrappers already do.The signing errors carry the same context as their sending counterparts, including the non-enumerable
transactionPlanResultand the optional simulationlogsandpreflightData. Those simulation fields are populated for signing too, because executors typically estimate resource limits by simulating before they sign, so a failed estimation reaches the error the same way it does when sending.They differ from the sending errors in one respect: the message carries no indicator of where the failure happened. That indicator exists to locate a failure relative to network submission —
(preflight)before it, or the transaction signature after it — and signing never submits, so neither applies. A signature would be particularly misleading, since quoting one implies the transaction reached the network when it never did. ThelogsandpreflightDatacontext properties are still populated whenever a simulation was responsible, and those logs still appear in the message, so only the prefix is dropped.[
@solana/plugin-interfaces] #189982a88d8Thanks @mcintyre94! - Add aClientWithTransactionSigninginterface providingsignTransactionandsignTransactions. These accept the same flexible inputs as theirClientWithTransactionSendingcounterparts, but hand back the signed transactions instead of submitting them. The interface is parameterised over the context attached to its results and makes no default guarantees about that context: what it contains is entirely decided by the plugin providing the capability — typically acontext.transactionon successful results.ClientWithTransactionSendingnow also accepts an optionalTContexttype parameter that flows through to the results ofsendTransactionandsendTransactions. Unlike the signing interface, it defaults toTransactionPlanResultContextWithSignaturefor backward compatibility, so existing usage keeps the requiredcontext.signatureon successful results.[
@solana/rpc-api,@solana/rpc-graphql,@solana/rpc-transformers,@solana/rpc-types,@solana/transaction-messages] #195194adb60Thanks @amilz! - Fill in several gaps in transaction v1 (SIMD-0385) support.@solana/transaction-messagesnow exports itsv1-transaction-configmodule, sosetTransactionMessageConfig, theV1TransactionConfigtype and the transaction config bit-mask helpers are importable. Previously the module was built and shipped but omitted from the package index, forcing consumers onto the four single-field setters and to derive the config type by hand.The
V1TransactionConfig.computeUnitLimitdocstring incorrectly described the legacy fallback of 200,000 compute units per instruction. On version 1 an unsetcomputeUnitLimitresolves to zero and the transaction fails at execution, so the docstring now says so, as does the one forloadedAccountsDataSizeLimit.@solana/rpc-typestransaction message types now carry thetransactionConfigthat the server returns for version 1 transactions, so reading a transaction's compute budget no longer requires a cast. Its threeu32fields are typed and transformed asnumberrather than being upcast tobigint, leavingpriorityFeeas the onlyLamportsamong them.@solana/rpc-apicarries the same field on the message shape it uses for thejsonandjsonParsedencodings ofgetTransactionandgetTransactionsForAddress. The same field is exposed on theTransactionMessagetype in@solana/rpc-graphql.[
@solana/transaction-messages] #1950ca01807Thanks @mcintyre94! - Add support for version 1 transaction messages tocreateTransactionMessage. You can now pass{ version: 1 }to create an empty v1 transaction message.This means that code using version 1 transaction messages will now type check.
Patch Changes
[
@solana/codecs-core] #19571b30374Thanks @mcintyre94! - FixtoArrayBufferreturning the entire backing buffer when given aUint8Arrayview that starts at byte offset zero but is shorter than its underlyingArrayBuffer. This causedsignBytesandverifySignatureto operate on the wrong bytes — andgetBase64Decoder().decode()to include trailing data — for such views, most notably themessageBytesof a decoded version 1 transaction, whose wire envelope places the message first and the signatures last[
@solana/transactions] #19585d526f7Thanks @mcintyre94! - FixgetTransactionSizeLimitmisclassifying single-signer legacy transactions as version 1. A legacy message has no version byte, so its first byte is the number of required signatures —1for every single-signer transaction — which was mistaken for a v1 version byte. As a result,isTransactionWithinSizeLimit,assertIsTransactionWithinSizeLimit, andassertIsSendableTransactionaccepted legacy transactions of up to 4096 bytes that the network rejects at 1232 bytes. The version flag (high bit) of the first message byte is now required to be set before treating a transaction as version 1
- 7.x
7.1.1
18 Aug 2026[
@solana/codecs-strings] #19261d074edThanks @latent-9! - FixgetBaseXDecoderreturning an offset of0instead of the buffer length when there are no bytes left to decode, which corrupted the offset of any decoder composed after it.[
@solana/instruction-plans] #1924769da66Thanks @mcintyre94! - Deprecate transaction plan result APIs being removed in v8Both of the following are deprecated and will be removed in the next major version.
successfulSingleTransactionPlanResultFromTransactionderives the transactionsignatureon your behalf by callinggetSignatureFromTransaction, which throws when the transaction's fee payer has not signed it. Construct results withsuccessfulSingleTransactionPlanResultinstead, passing the context explicitly:- successfulSingleTransactionPlanResultFromTransaction(message, transaction, context); + successfulSingleTransactionPlanResult(message, { + ...context, + signature: getSignatureFromTransaction(transaction), + transaction, + });BaseTransactionPlanResultContextgoes away together with the intersections that graft it onto everySingleTransactionPlanResult. The context of a result is becoming entirely caller-defined — it will be exactly theTContextyou supply — so there will be no separate base shape to merge in. If you refer to this type, declare whichever of its fields you need on your own context type instead:type MyContext = { message?: TransactionMessage & TransactionMessageWithFeePayer; signature?: Signature; transaction?: Transaction; };[
@solana/rpc-spec,@solana/rpc-spec-types,@solana/rpc-subscriptions-spec] #17489f8e4d0Thanks @ChargingFoxSec! - Avoid treating JavaScript protocol hooks and Object prototype properties as RPC method names in proxy-backed RPC objects.
@solana/kit
Patch Changes
[
@solana/codecs-strings] #19261d074edThanks @latent-9! - FixgetBaseXDecoderreturning an offset of0instead of the buffer length when there are no bytes left to decode, which corrupted the offset of any decoder composed after it.[
@solana/instruction-plans] #1924769da66Thanks @mcintyre94! - Deprecate transaction plan result APIs being removed in v8Both of the following are deprecated and will be removed in the next major version.
successfulSingleTransactionPlanResultFromTransactionderives the transactionsignatureon your behalf by callinggetSignatureFromTransaction, which throws when the transaction's fee payer has not signed it. Construct results withsuccessfulSingleTransactionPlanResultinstead, passing the context explicitly:- successfulSingleTransactionPlanResultFromTransaction(message, transaction, context); + successfulSingleTransactionPlanResult(message, { + ...context, + signature: getSignatureFromTransaction(transaction), + transaction, + });BaseTransactionPlanResultContextgoes away together with the intersections that graft it onto everySingleTransactionPlanResult. The context of a result is becoming entirely caller-defined — it will be exactly theTContextyou supply — so there will be no separate base shape to merge in. If you refer to this type, declare whichever of its fields you need on your own context type instead:type MyContext = { message?: TransactionMessage & TransactionMessageWithFeePayer; signature?: Signature; transaction?: Transaction; };[
@solana/rpc-spec,@solana/rpc-spec-types,@solana/rpc-subscriptions-spec] #17489f8e4d0Thanks @ChargingFoxSec! - Avoid treating JavaScript protocol hooks and Object prototype properties as RPC method names in proxy-backed RPC objects.[
@solana/rpc-subscriptions-api,@solana/rpc-subscriptions-spec,@solana/rpc-transformers] #192590e371bThanks @o-mid! - Consult the subscriptions numeric allow-list for each notificationThe allow-list is keyed by API names like
blockNotifications, but the plan executor invoked the transformer with the rewritten subscribe request (blockSubscribe). The lookup missed and every notification numeric was upcast tobigintwhile still typechecking asnumber.The transformer now maps
*Subscribe/*Notificationnames back to*Notifications, and the plan executor derives the method name from each notification payload so subscriptions that share a channel are not transformed under whichever request created the publisher.blockNotificationsis also derived from the sameinnerInstructionsConfigs/messageConfig/tokenBalancesConfigsasgetBlock, so transactionversion, token balanceuiAmount, andstackHeightstay numbers.[
@solana/signers] #18510a989a4Thanks @rajanpanth! - FixTransactionMessageWithSignersso its signer type parameter rejects fee payer and instruction signers of other transaction signer kinds.
7.1.0
14 Aug 2026[
@solana/errors,@solana/kit,@solana/react,@solana/subscribable] #18117022c26Thanks @mcintyre94! - AddbridgeStoreToAsyncIterableto@solana/subscribablebridgeStoreToAsyncIterableadapts aReactiveStreamStoreinto the pull-basedAsyncIterablecontract that consumers like TanStack Query'sexperimental_streamedQueryexpect. It is now a public export of@solana/subscribable(and re-exported from@solana/kit). It was previously an internal helper of@solana/react, but it is not React- or TanStack-specific and is useful to any consumer that needs to drive a stream store byfor await-ing it.The bridge only observes the store — consistent with the rest of the ecosystem, the caller owns the store's lifecycle (
connect()it yourself, bound to the same signal, andreset()it when done). The bridge subscribes, seeds from the store's current snapshot, yields values, and unsubscribes when iteration ends.It throws the new
SOLANA_ERROR__SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERRORwhen a store closes in an error state with a nullish payload. This is the erroruseSubscriptionQueryanduseTrackedDataQuerynow surface in that case; the SWR bridge is unaffected.[
@solana/errors,@solana/offchain-messages] #188814a3e5bThanks @mcintyre94! - Add anassertOffchainMessageV1Equalhelper that asserts that a version 1 offchain message you received from an untrusted signer (eg. a wallet) is the message you expected it to sign. Verifying a signature proves only that the signer produced it over the bytes it handed back, not that those bytes represent the message you asked for, so assert this before verifying signatures withverifyOffchainMessageEnvelope. The helper compares the content and the required signatories, and reports each kind of mismatch with its own error code: the newSOLANA_ERROR__OFFCHAIN_MESSAGE__CONTENT_DOES_NOT_MATCH_EXPECTEDandSOLANA_ERROR__OFFCHAIN_MESSAGE__REQUIRED_SIGNATORIES_DO_NOT_MATCH_EXPECTED. Required signatories are compared without regard to order, since a decoded message lists them in the order the specification mandates while yours may be in any order. It accepts anOffchainMessageV1rather than theOffchainMessageunion that decoding produces, so narrow the decoded message to a version 1 message before calling it.[
@solana/instruction-plans] #19159e7daeaThanks @mcintyre94! - Let thecreateTransactionPlanExecutorcallback return the context of a successful resultThe
executeTransactionMessagecallback may now return the context that a successful result should carry, instead of aSignatureor aTransaction. When it does, that context is used as-is: nothing is derived from it, and in particulargetSignatureFromTransactionis never called on your behalf.const transactionPlanExecutor = createTransactionPlanExecutor({ executeTransactionMessage: async (context, message) => { const transaction = await signTransactionMessageWithSigners(message); context.transaction = transaction; + const signature = getSignatureFromTransaction(transaction); await sendAndConfirmTransaction(transaction, { commitment: 'confirmed' }); - return transaction; + return { signature, transaction }; }, });Since a successful result always carries a signature, a returned context must include one — a callback that declares a custom context and forgets a property of it now fails to compile, rather than producing a result whose context is typed but
undefinedat runtime. That signature is also how the executor tells a returned context apart from a returnedTransaction, which keeps its signatures in asignaturesmap and therefore never has one.The mutable
contextargument is unchanged and still serves the failure path: whatever the callback stores on it before it throws is preserved in the resultingFailedSingleTransactionPlanResult. On success the two are merged, with the returned context taking precedence, so a property stored but not returned is still reported.Returning a
Signatureor aTransactionis deprecated. Both still behave exactly as before — a returned signature is stored ascontext.signature, and a returned transaction is stored ascontext.transactionwith its signature derived from it — and IDEs now flag those call sites, becausecreateTransactionPlanExecutorgained a deprecated overload that only matches callbacks returning those types. Note that a config declared asTransactionPlanExecutorConfigup front is not flagged, since that type permits either return style.Prefer returning a context, since deriving a signature from a transaction throws
SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSINGwhen the fee payer slot is empty. An executor that deliberately produces partially signed transactions — signed by an authority, to be paid for and submitted by a relayer later — can now succeed by returning its own signature alongside the transaction. Dropping the signature from a successful result's context altogether remains impossible, sinceSuccessfulSingleTransactionPlanResultguarantees one.Failure handling is unchanged, including the signature still derived from a
transactionleft on the context when the callback throws. Since the callback never returned anything in that case, there is nothing to bypass that derivation, so a callback working with fee-payer-unsigned transactions should avoid storing them on the context — otherwise deriving a signature from one replaces the error it meant to report.
@solana/kit
Minor Changes
[
@solana/errors,@solana/kit,@solana/react,@solana/subscribable] #18117022c26Thanks @mcintyre94! - AddbridgeStoreToAsyncIterableto@solana/subscribablebridgeStoreToAsyncIterableadapts aReactiveStreamStoreinto the pull-basedAsyncIterablecontract that consumers like TanStack Query'sexperimental_streamedQueryexpect. It is now a public export of@solana/subscribable(and re-exported from@solana/kit). It was previously an internal helper of@solana/react, but it is not React- or TanStack-specific and is useful to any consumer that needs to drive a stream store byfor await-ing it.The bridge only observes the store — consistent with the rest of the ecosystem, the caller owns the store's lifecycle (
connect()it yourself, bound to the same signal, andreset()it when done). The bridge subscribes, seeds from the store's current snapshot, yields values, and unsubscribes when iteration ends.It throws the new
SOLANA_ERROR__SUBSCRIBABLE__STREAM_CLOSED_WITHOUT_ERRORwhen a store closes in an error state with a nullish payload. This is the erroruseSubscriptionQueryanduseTrackedDataQuerynow surface in that case; the SWR bridge is unaffected.[
@solana/errors,@solana/offchain-messages] #188814a3e5bThanks @mcintyre94! - Add anassertOffchainMessageV1Equalhelper that asserts that a version 1 offchain message you received from an untrusted signer (eg. a wallet) is the message you expected it to sign. Verifying a signature proves only that the signer produced it over the bytes it handed back, not that those bytes represent the message you asked for, so assert this before verifying signatures withverifyOffchainMessageEnvelope. The helper compares the content and the required signatories, and reports each kind of mismatch with its own error code: the newSOLANA_ERROR__OFFCHAIN_MESSAGE__CONTENT_DOES_NOT_MATCH_EXPECTEDandSOLANA_ERROR__OFFCHAIN_MESSAGE__REQUIRED_SIGNATORIES_DO_NOT_MATCH_EXPECTED. Required signatories are compared without regard to order, since a decoded message lists them in the order the specification mandates while yours may be in any order. It accepts anOffchainMessageV1rather than theOffchainMessageunion that decoding produces, so narrow the decoded message to a version 1 message before calling it.[
@solana/instruction-plans] #19159e7daeaThanks @mcintyre94! - Let thecreateTransactionPlanExecutorcallback return the context of a successful resultThe
executeTransactionMessagecallback may now return the context that a successful result should carry, instead of aSignatureor aTransaction. When it does, that context is used as-is: nothing is derived from it, and in particulargetSignatureFromTransactionis never called on your behalf.const transactionPlanExecutor = createTransactionPlanExecutor({ executeTransactionMessage: async (context, message) => { const transaction = await signTransactionMessageWithSigners(message); context.transaction = transaction; + const signature = getSignatureFromTransaction(transaction); await sendAndConfirmTransaction(transaction, { commitment: 'confirmed' }); - return transaction; + return { signature, transaction }; }, });Since a successful result always carries a signature, a returned context must include one — a callback that declares a custom context and forgets a property of it now fails to compile, rather than producing a result whose context is typed but
undefinedat runtime. That signature is also how the executor tells a returned context apart from a returnedTransaction, which keeps its signatures in asignaturesmap and therefore never has one.The mutable
contextargument is unchanged and still serves the failure path: whatever the callback stores on it before it throws is preserved in the resultingFailedSingleTransactionPlanResult. On success the two are merged, with the returned context taking precedence, so a property stored but not returned is still reported.Returning a
Signatureor aTransactionis deprecated. Both still behave exactly as before — a returned signature is stored ascontext.signature, and a returned transaction is stored ascontext.transactionwith its signature derived from it — and IDEs now flag those call sites, becausecreateTransactionPlanExecutorgained a deprecated overload that only matches callbacks returning those types. Note that a config declared asTransactionPlanExecutorConfigup front is not flagged, since that type permits either return style.Prefer returning a context, since deriving a signature from a transaction throws
SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSINGwhen the fee payer slot is empty. An executor that deliberately produces partially signed transactions — signed by an authority, to be paid for and submitted by a relayer later — can now succeed by returning its own signature alongside the transaction. Dropping the signature from a successful result's context altogether remains impossible, sinceSuccessfulSingleTransactionPlanResultguarantees one.Failure handling is unchanged, including the signature still derived from a
transactionleft on the context when the callback throws. Since the callback never returned anything in that case, there is nothing to bypass that derivation, so a callback working with fee-payer-unsigned transactions should avoid storing them on the context — otherwise deriving a signature from one replaces the error it meant to report.[
@solana/kit] #18984a5f717Thanks @lorisleiva! - Add helpers to create client interfaces from a rawRpcAdd
createClientWithGetMinimumBalanceFromRpc,createClientWithFetchAccountsFromRpcandcreateClientWithInterfacesFromRpcto@solana/kit. These convenience helpers let consumers that only have a rawRpcobject construct the corresponding client interfaces (ClientWithGetMinimumBalanceandClientWithFetchAccounts) without assembling a full Kit client.createClientWithInterfacesFromRpcfills in whichever interfaces the RPC supports and narrows its return type accordingly.[
@solana/kit] #1824b47feb6Thanks @mcintyre94! - Re-export@solana/promisesfrom@solana/kit@solana/kitnow re-exports the@solana/promisespackage, so its helpers —isAbortError,getAbortablePromise, andsafeRace— are available directly from@solana/kitwithout a separate dependency. This is particularly useful alongside@solana/react'suseAction, whose superseded or aborted dispatches reject with anAbortErrorthat callers filter usingisAbortError.[
@solana/plugin-interfaces] #1897aa0b625Thanks @lorisleiva! - Add aClientWithFetchAccountsinterfaceThis new plugin interface represents a client that can fetch the encoded content of accounts from their addresses via a
fetchAccounts(addresses, config?)method. Like the other@solana/plugin-interfacescapabilities, it lets plugins provide or require account-fetching without coupling to a concrete RPC. The returned array matches the provided addresses in length and order, usingMaybeEncodedAccountto represent accounts that may not exist.[
@solana/react] #1876d6a1adbThanks @mcintyre94! - AddusePayeranduseIdentityReact hooks. Each reads the corresponding value off the client and, when the client advertisessubscribeToPayer/subscribeToIdentity, subscribes so the returned signer always reflects the latest payer/identity. Clients whose value is fixed fall back to a one-time read.If the plugin value throws (for example as the wallet plugin does when it owns payer/identity and a wallet is not connected), this is surfaced as
undefinedin the hooks.[
@solana/react] #184194f49bbThanks @mcintyre94! - Make theTClienttype parameter ofuseClientrequired by removing itsobjectdefault, matchinguseClientCapability. Callers should always pass their client's shape (typically an exportedAppClienttype) so installed capabilities are typed at the call site.- const client = useClient(); + const client = useClient<AppClient>();
[
@solana/react] #18692193459Thanks @mcintyre94! - AddusePlanTransaction,usePlanTransactions,useSendTransaction, anduseSendTransactionshooks for driving a client's transaction-planning and -sending capabilities as reactive actions.[
@solana/react] #1879c27ce2fThanks @mcintyre94! - Add auseAirdrophook that wraps a client'sairdropcapability (ClientWithAirdrop) as a trackeduseAction.dispatch(address, amount)requests an airdrop with an injectedAbortSignal, resolving with the transactionSignature(orundefinedwhen the airdrop is applied without a transaction).[
@solana/rpc-api] #1776c8235caThanks @mcintyre94! - Add thegetTransactionsForAddressRPC method type. This method combines address-history discovery and per-transaction fetching into a single query, with server-side filtering, bidirectional sorting, and cursor-based pagination. It will be part of the upcoming solana-rpc spec and is part of thesolana-rpc/superbankproject, and is already available from major RPC providers.It supports both
signaturesandfull(json/jsonParsed/base58/base64) response modes. The shared transaction metadata types also gain an optionalmeta.costUnitsfield, which surfaces ongetTransactionas well.[
@solana/rpc-transformers] #191980b3756Thanks @amilz! - Stop upcasting token balanceuiAmountand related numerics tobigintThe response transformer upcasts every JSON integer to a
bigintunless its keypath appears in an allow-list. Because the upcast only applies to integers,uiTokenAmount.uiAmount— anf64on the server — arrived as abigintwhen the balance happened to be a whole number and as anumberwhen it was fractional, so its declared type was correct for some values and wrong for others.uiTokenAmount.uiAmountis now allow-listed ongetTransaction,getBlock, andgetTransactionsForAddresstoken balances.simulateTransactionhad no token balance keypaths allow-listed at all, soaccountIndexanduiTokenAmount.decimalswere upcast there as well. All three are now allow-listed.
@solana/rpc-transformersadditionally exports a newtokenBalancesConfigsarray of token-balance-relative keypaths, alongside the existinginnerInstructionsConfigsandmessageConfig.[
@solana/transaction-introspection] #1814c45d5e0Thanks @mcintyre94! -decodeTransactionFromRpcResponsenow accepts confirmed transactions from any RPC method that returns them, not justgetTransaction. It reads only the sharedtransaction/meta/versionenvelope, sogetTransactionsForAddressresults (map over itsdataarray) andgetBlockresults (map over itstransactionsarray, withtransactionDetails: 'full') decode identically, including legacy transactions fetched withoutmaxSupportedTransactionVersion. The'json'overload now types its omittedtransactionasneverrather than an optionalTransaction, reflecting that the JSON path never yields re-encodable wire bytes.
Patch Changes
[
@solana/codecs-data-structures] #19118c9eeceThanks @latent-9! - FixgetBitArrayEncoderreturning the wrong next offset. Itswritereturnedsizeinstead ofoffset + size, so a bit array placed before another field in a struct or tuple was overwritten by the following field. It now returnsoffset + size, matching the decoder and the other codecs.[
@solana/codecs-data-structures] #1884da10c5aThanks @Swift42! - Avoid copying the remaining buffer ingetArrayDecoder's emptiness checkgetArrayDecoder'sread()tested for an empty byte array withbytes.slice(offset).length === 0, which allocates and copies every byte fromoffsetto the end just to read.lengthoff the result. On large accounts containing many prefixed arrays, maps, or sets this made decoding quadratic in account size. The check is now the equivalent O(1) comparisonoffset >= bytes.length.getMapDecoderandgetSetDecoderdelegate togetArrayDecoderand benefit as well.[
@solana/codecs-data-structures] #1809204ed6eThanks @mcintyre94! - Allow boolean predicates passed togetPatternMatchCodecandgetPatternMatchEncoderto narrow to a subtype of the variant's value type. Previously, matching against codecs whose value type is a union — such as the number codecs, whose encode type isnumber | bigint— forced predicates to be typed against the full union (e.g.(value: number | bigint) => …). The predicate parameter is now checked bivariantly, so a narrower predicate like(value: number) => …is accepted, mirroring the ergonomics ofgetPredicateCodecandgetPredicateEncoder.[
@solana/kit,@solana/plugin-core] #1883a900eebThanks @mcintyre94! - FixwithCleanupthrowingDisposableStack is not definedon SafariwithCleanupconstructed aDisposableStackunconditionally, but Safari has not shipped explicit resource management — as of Safari 27 it provides neitherDisposableStacknorSymbol.dispose— so any plugin that registers a cleanup function threwReferenceError: Can't find variable: DisposableStackwhile the client was being built.The runtime's own
DisposableStackis still used whenever it exists. Only where it is missing doeswithCleanupfall back to an internal stack that reproduces the behaviour it depends on. ThewithCleanuptest suite now runs twice, once against each stack, so the two cannot drift apart.Note that this fixes disposal on Safari but not
usingdeclarations in your own code, which additionally need aSymbol.disposepolyfill; disposing a client explicitly works either way.[
@solana/react] #19079d6be07Thanks @mcintyre94! - Widen the@solana/kitpeer dependency of@solana/reactfrom an exact version to a caret range.@solana/reactpreviously declared"@solana/kit": "workspace:*", which publishes as an exact pin ("@solana/kit": "7.0.0"), so a consumer who advanced@solana/kitwithout advancing@solana/reactin the same step hit an unsatisfiable peer range even though the two are compatible. It now declaresworkspace:^and publishes as^7.1.0. The two packages continue to be released in lockstep at identical versions, so this does not loosen which combinations are actually shipped — it only stops describing a compatible pair as incompatible.[
@solana/react] #1825d54b899Thanks @mcintyre94! - Bump the@wallet-standard/uiand@wallet-standard/ui-registrydependencies to^1.0.3and^1.1.1respectively. The1.1.xregistry line is a backward-compatible superset that continues to export the names@solana/reactrelies on, and aligning with it lets consumers that also pull in@solana/kit-plugin-walletresolve a single, shared copy of the wallet-standard UI registry (which is a runtime singleton) instead of splitting across two incompatible copies.[
@solana/rpc-api] #191980b3756Thanks @amilz! - Stop upcasting token balanceuiAmountand related numerics tobigintThe response transformer upcasts every JSON integer to a
bigintunless its keypath appears in an allow-list. Because the upcast only applies to integers,uiTokenAmount.uiAmount— anf64on the server — arrived as abigintwhen the balance happened to be a whole number and as anumberwhen it was fractional, so its declared type was correct for some values and wrong for others.uiTokenAmount.uiAmountis now allow-listed ongetTransaction,getBlock, andgetTransactionsForAddresstoken balances.simulateTransactionhad no token balance keypaths allow-listed at all, soaccountIndexanduiTokenAmount.decimalswere upcast there as well. All three are now allow-listed.
@solana/rpc-transformersadditionally exports a newtokenBalancesConfigsarray of token-balance-relative keypaths, alongside the existinginnerInstructionsConfigsandmessageConfig.[
@solana/rpc-api] #191782c4cebThanks @amilz! - Stop upcasting transactionversiontobigintThe response transformer upcasts every JSON integer to a
bigintunless its keypath appears in an allow-list.versionwas missing from that allow-list ongetTransaction,getBlocktransactions, andgetTransactionsForAddress, so it arrived at runtime as0nwhile still typechecking asTransactionVersion('legacy' | 0 | 1).A check like
if (transaction.version === 0)therefore compiled cleanly and was always false, with no compiler error and no runtime error. The keypath is now allow-listed andversionarrives as a number, matching its declared type.[
@solana/transaction-messages] #1874327760cThanks @mcintyre94! - Update type of compressTransactionMessageUsingAddressLookupTables to reject v1 transactions
7.0.0
30 Jun 2026Reading the note…
- 6.x
6.10.0
16 Jun 2026Reading the note…
6.9.0
6 May 2026Reading the note…
6.8.0
10 Apr 2026Reading the note…
6.7.0
2 Apr 2026Reading the note…
6.6.0
1 Apr 2026Reading the note…
6.5.0
19 Mar 2026Reading the note…
6.4.0
18 Mar 2026Reading the note…
6.3.1
12 Mar 2026Reading the note…
6.3.0
11 Mar 2026Reading the note…
6.2.0
9 Mar 2026Reading the note…
6.1.0
16 Feb 2026Reading the note…
6.0.1
5 Feb 2026Reading the note…
6.0.0
4 Feb 2026Reading the note…
- 5.x
5.5.1
28 Jan 2026Reading the note…
5.5.0
27 Jan 2026Reading the note…
5.4.0
13 Jan 2026Reading the note…
5.3.0
7 Jan 2026Reading the note…
5.2.0
7 Jan 2026Reading the note…
5.1.0
12 Dec 2025Reading the note…
5.0.0
23 Oct 2025Reading the note…
- 4.x
4.0.0
8 Oct 2025Reading the note…
- 3.x
3.0.3
11 Sep 2025Reading the note…
3.0.2
3 Sep 2025Reading the note…
3.0.1
29 Aug 2025Reading the note…
3.0.0
27 Aug 2025Reading the note…
- 2.x
2.3.0
8 Jul 2025Reading the note…
2.2.1
8 Jul 2025Reading the note…
2.2.0
8 Jul 2025Reading the note…
2.1.1
12 May 2025Reading the note…
2.1.0
27 Feb 2025Reading the note…
2.0.0
7 Nov 2024Reading the note…