github anza-xyz/kit v6.10.0

8 hours ago

@solana/kit

v6.10.0 (2026-06-16)

Minor Changes

  • [@solana/errors, @solana/kit, @solana/subscribable] #1552 c318d7f Thanks @mcintyre94! - Add retry() and getUnifiedState() to ReactiveStore. The new getUnifiedState() returns a discriminated { data, error, status } snapshot with stable identity, so stores can be passed directly to useSyncExternalStore without an intermediate wrapper. getState() and getError() remain on the type but are now @deprecated in favour of the unified snapshot.

    A new createReactiveStoreFromDataPublisherFactory function is also introduced. It accepts a createDataPublisher: () => Promise<DataPublisher> factory rather than a ready-made publisher, which lets the store reconnect via retry() after an error. The existing createReactiveStoreFromDataPublisher is now @deprecated; calling retry() on a store it produced throws a new SolanaError with code SOLANA_ERROR__SUBSCRIBABLE__RETRY_NOT_SUPPORTED.

    createReactiveStoreWithInitialValueAndSlotTracking (from @solana/kit) now supports retry(), which re-sends the RPC request and re-subscribes to the subscription with a fresh abort signal while preserving the last known slot and value.

  • [@solana/errors, @solana/kit] #1654 460557b Thanks @mcintyre94! - Added estimateResourceLimitsFactory, estimateAndSetResourceLimitsFactory, and fillTransactionMessageProvisoryResourceLimits to @solana/kit. These mirror the existing compute-unit estimators but additionally estimate and set the loaded accounts data size limit, which is required for version 1 transactions. Both limits are derived from a single simulation call.

    Two new error codes were added to @solana/errors: SOLANA_ERROR__TRANSACTION__FAILED_TO_ESTIMATE_LOADED_ACCOUNTS_DATA_SIZE_LIMIT (thrown when an RPC fails to return a loadedAccountsDataSize value while estimating a version 1 transaction) and SOLANA_ERROR__TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_RESOURCE_LIMITS (the resource-limits counterpart of SOLANA_ERROR__TRANSACTION__FAILED_WHEN_SIMULATING_TO_ESTIMATE_COMPUTE_LIMIT).

    Migration

    The compute-unit-only helpers are still exported but are now deprecated. The new helpers handle every transaction version: for legacy and version 0 messages they behave the same as the old ones (only the compute unit limit is set); for version 1 messages they additionally set the loaded accounts data size limit, which is required for v1.

    estimateComputeUnitLimitFactoryestimateResourceLimitsFactory

    The new estimator returns an object instead of a number. Destructure computeUnitLimit from the result:

    // Before
    const estimateComputeUnitLimit = estimateComputeUnitLimitFactory({ rpc });
    const units = await estimateComputeUnitLimit(transactionMessage);
    
    // After
    const estimateResourceLimits = estimateResourceLimitsFactory({ rpc });
    const { computeUnitLimit } = await estimateResourceLimits(transactionMessage);
    // If provided by the RPC, `loadedAccountsDataSizeLimit` is also returned

    estimateAndSetComputeUnitLimitFactoryestimateAndSetResourceLimitsFactory

    The new helper accepts the multi-resource estimator and returns a function with the same shape as before — it takes a transaction message and returns the same message with resource limits set. No call-site change beyond the factory swap:

    // Before
    const estimator = estimateComputeUnitLimitFactory({ rpc });
    const estimateAndSet = estimateAndSetComputeUnitLimitFactory(estimator);
    
    // After
    const estimator = estimateResourceLimitsFactory({ rpc });
    const estimateAndSet = estimateAndSetResourceLimitsFactory(estimator);

    Behavior note: the new helper re-estimates the compute unit limit when it is unset, set to the provisory value of 0, or set to the runtime max of 1_400_000 (same as before). For the loaded accounts data size limit on v1 messages it only re-estimates when unset or set to the provisory 0; an explicit value — including the runtime max of 64 MiB — is left untouched, since callers who set it explicitly are signaling a deliberate choice.

    fillTransactionMessageProvisoryComputeUnitLimitfillTransactionMessageProvisoryResourceLimits

    The signature is unchanged. For v1 messages, the new helper additionally reserves a provisory 0 for the loaded accounts data size limit when none is set. For legacy and v0 messages, the behavior is unchanged and the function only reserves space for the CU limit.

    // Before
    const reserved = fillTransactionMessageProvisoryComputeUnitLimit(transactionMessage);
    
    // After
    const reserved = fillTransactionMessageProvisoryResourceLimits(transactionMessage);
  • [@solana/errors] #1746 40e0848 Thanks @mcintyre94! - Add the SOLANA_ERROR__WALLET__ACCOUNT_NOT_AVAILABLE error code, thrown when a selected account is not available in the connected wallet. Its context carries the requested address and the walletName.

  • [@solana/errors, @solana/kit, @solana/rpc-subscriptions-spec, @solana/subscribable] #1554 47a785b Thanks @mcintyre94! - Rename ReactiveStore<T> to ReactiveStreamStore<T>. The old name remains exported as a deprecated alias and will be removed in a future major release.

  • [@solana/kit, @solana/rpc-spec] #1555 5e1644d Thanks @mcintyre94! - Add a reactiveStore() method to PendingRpcRequest. It fires the request on construction and synchronously returns a ReactiveActionStore that holds the request's idle/running/success/error lifecycle state. Compatible with useSyncExternalStore, Svelte stores, and other reactive primitives. Call dispatch() to re-fire the request (e.g. after an error), or reset() to abort the in-flight call and return to idle.

    const store = rpc.getAccountInfo(address).reactiveStore();
    const state = useSyncExternalStore(store.subscribe, store.getState);
    if (state.status === 'error') return <ErrorMessage error={state.error} onRetry={store.dispatch} />;
    if (state.status === 'running' && !state.data) return <Spinner />;
    return <View data={state.data!} />;
  • [@solana/kit, @solana/rpc-subscriptions-spec] #1553 15b610d Thanks @mcintyre94! - Add a reactiveStore() method to PendingRpcSubscriptionsRequest. Unlike reactive(), this variant returns a ReactiveStore synchronously and supports retry() to reconnect after an error. reactive() is now @deprecated in favour of reactiveStore().

    const store = rpc.accountNotifications(address).reactiveStore({ abortSignal });
    const state = useSyncExternalStore(store.subscribe, store.getUnifiedState);
    if (state.status === 'error') return <ErrorMessage error={state.error} onRetry={store.retry} />;
  • [@solana/kit, @solana/subscribable] #1606 da868aa Thanks @mcintyre94! - Add framework-agnostic source duck-types for reactive bindings.

    @solana/subscribable now exports two new types:

    • ReactiveStreamSource<T> — anything with a reactiveStore({ abortSignal }) method that returns a ReactiveStreamStore<T>. PendingRpcSubscriptionsRequest<T> satisfies this by design.
    • ReactiveActionSource<T> — anything with a zero-argument reactiveStore() method that returns a ReactiveActionStore<[], T>. PendingRpcRequest<T> satisfies this by design.

    These let reactive-framework bindings consume a single duck-type instead of naming concrete producer types — and let plugin authors expose their own pending-request objects to those bindings without modification.

    Both source types live in @solana/subscribable and are not re-exported from @solana/kit, matching the existing convention for their parent ReactiveStreamStore / ReactiveActionStore types — anyone consuming a source duck-type is already in the reactive-primitives layer and will already be importing the related store types from the same package.

    @solana/kit now publicly exports the previously-private CreateReactiveStoreWithInitialValueAndSlotTrackingConfig type so non-React consumers (e.g. plugins) can declare function return shapes based on it without taking a dependency on @solana/react.

  • [@solana/rpc-api] #1660 0d5aa7f Thanks @kh0ra! - Add the optional transactionIndex field to each element of the getSignaturesForAddress response. Agave 4.0 (anza-xyz/agave#9683) included the 0 based transaction index inside the block alongside each returned signature. The field is omitted by older RPC servers, so the new property is optional and existing call sites continue to compile without modification.

  • [@solana/rpc-api] #1658 d655bef Thanks @mcintyre94! - Added the clientId field to the getClusterNodes response type, exposing the name of the validator client software advertised by each node. Also marked the tpu and tpuForwards fields as @deprecated in favor of their QUIC equivalents (tpuQuic and tpuForwardsQuic); validators may report the UDP fields as null.

  • [@solana/rpc-types] #1659 93191af Thanks @kh0ra! - Base58EncodedBytes, Base64EncodedBytes, and Base64EncodedZStdCompressedBytes are no longer uniquely branded. They are now plain EncodedString<...> aliases over their respective encoding tags. As a result, any value that already carries the matching encoding tag (such as Address, Signature, or Blockhash for base 58) can be used wherever Base58EncodedBytes is expected. For example, an Address may now be passed directly as the bytes of a memcmp filter in getProgramAccounts. The runtime representation is unchanged and existing call sites continue to compile without modification.

  • [@solana/subscribable] #1550 82a1ac5 Thanks @mcintyre94! - Added createReactiveActionStore — a framework-agnostic state machine that wraps an async function and exposes a { dispatch, dispatchAsync, getState, subscribe, reset } contract compatible with useSyncExternalStore, Svelte stores, Vue's shallowRef, and similar reactive primitives. dispatch is synchronous and fire-and-forget (safe from UI event handlers); dispatchAsync returns a promise that resolves to the wrapped function's result and rejects on failure or supersede — use isAbortError from @solana/promises to filter aborts. Each call creates a fresh AbortController and aborts the previous one, so rapid successive dispatches only produce one final state transition — the outcome of the most recent call.

Patch Changes

  • [@solana/errors] #1681 6b499ee Thanks @brooksprumo! - Add FILTER_TRANSACTION_NOT_FOUND error

  • [@solana/errors] #1679 74b8d3d Thanks @brooksprumo! - Add NO_SLOT_HISTORY error

  • [@solana/offchain-messages] #1741 6c2c903 Thanks @ChargingFoxSec! - Include the v0 preamble bytes when enforcing the 1232-byte limit for hardware-wallet-signable offchain messages.

  • [@solana/plugin-core] #1646 09e7796 Thanks @lorisleiva! - Flatten the inferred return type of extendClient and withCleanup so that chained .use() calls on a Client no longer produce deeply nested Omit<Omit<Omit<...>>> types in editor tooltips and error messages. The inferred shape now displays as a single flat object literal at every step of the chain, while optional (?) and readonly modifiers, symbol keys, and override semantics are preserved exactly as before. Also exports the new ExtendedClient<TClient, TAdditions> helper type for plugin authors who write their own merging helpers and want the same flattening guarantee.

  • [@solana/rpc-transport-http] #1700 bb67860 Thanks @pgtls! - Stop sending a manual Content-Length header from the HTTP transport. The value came from body.length — the JavaScript string's UTF-16 code-unit count — which is smaller than the body's actual UTF-8 byte length whenever the payload contains non-ASCII characters. For such payloads the server would respect the header, read only that many bytes off the socket, and stall waiting for a request that the client believed it had already finished sending. The symptom is a hung request rather than a thrown error, and it has been latent since the line was introduced; recent undici versions surface it more readily. The fix is to let fetch derive Content-Length from the body itself, which it always did anyway. The transport's TypeScript surface and dev-mode runtime check continue to disallow Content-Length as a caller-supplied header.

  • [@solana/transaction-confirmation] #1640 953eed6 Thanks @kh0ra! - Fix an abort listener leak in getTimeoutPromise. The listener registered on the caller's AbortSignal was never removed after the promise settled, which caused listeners to accumulate when the same signal was reused across multiple calls. getTimeoutPromise now registers the listener with the auto-cleanup signal option already used by the other strategies in this package and releases it in a finally block.

Don't miss a new kit release

NewReleases is sending notifications on new releases.