Yellowstone Account Sync
Faster account info queries with lower costs
Yellowstone Account Sync and the Triton SDKs
This guide explains how to read Solana accounts with the Triton JavaScript/TypeScript SDK (@triton-one/triton-sdk) and Rust SDK (triton-sdk) through the account-sync service.
You do not need to understand gRPC, WebSocket, or the service internals before you start.
TL;DR
If your application frequently reads the same Solana accounts, those reads can add RPC latency, request volume, and per-request costs as your application scales.
Account Sync streams account updates in the background and keeps the latest account state in memory. The SDKs expose familiar account-reading methods from @solana/web3.js and the asynchronous Solana Rust RpcClient.
Supported reads can use account state already held in memory. JSON-RPC still supplies initial values and fallback reads.
Account updates
Fetched on request
Streamed continuously
RPC processing latency
On every request
Avoided for cached account reads
Request rate limits
Apply
Not applicable to in-memory reads
Usage cost
RPC requests + bandwidth
Primarily streamed bandwidth
JavaScript benchmark
In our JavaScript getMultipleAccountsInfo benchmark, Account Sync reduced median read latency from 12.01 ms to 1.65 ms and p90 read latency from 14.94 ms to 1.98 ms.
@solana/web3.js RPC
12.01 ms
14.94 ms
Account Sync
1.65 ms
1.98 ms
Reduction
86.3%
86.8%
See the JavaScript benchmark and Rust timing example. They measure account read duration, not stream delivery latency. A successful read alone does not prove that the result came from the stream.
What the SDKs do
Solana stores programs and user state in accounts. Each account has an address, a balance, an owner, and some data bytes.
Each SDK adds a live account stream:
Your application tells the SDK which accounts it needs.
The account-sync service sends updates for those accounts.
The SDK keeps the newest observed value for each tracked account in memory.
Supported account read methods use that local value when possible.
The SDK also uses JSON-RPC to load initial state, resolve a missing local value, and keep reads working while the stream is down.
The in-memory copy is called the buffer in this guide.
The main benefit is that repeated reads can use data already held by your application instead of making a new JSON-RPC request each time.
When to use Account Sync
Account Sync is designed for applications that frequently poll the same accounts and benefit from having the latest account state available with low read latency.
A good fit
Account Sync is a good fit if your application:
Frequently reads the same accounts.
Polls accounts to keep application state up to date.
Needs fresh account data with low read latency.
Makes enough account RPC requests that request volume or RPC costs are significant.
In these workloads, Account Sync replaces repeated RPC reads with streamed account updates and serves subsequent reads from memory. This can reduce both read latency and RPC request costs.
When standard RPC may be a better fit
Standard RPC calls may be more appropriate if your application:
Reads account data infrequently.
Does not require account state to stay continuously up to date.
Only needs fresh account data in response to occasional user actions or events.
Account Sync continuously receives updates for subscribed accounts, even when your application is not reading them. If reads are infrequent, the bandwidth cost of maintaining the stream may outweigh the savings from avoiding individual RPC requests.
Rule of thumb: The more frequently you poll the same accounts, the more Account Sync can reduce repeated RPC work. For occasional reads, fetching the data on demand is typically a better fit.
SDK and transport support
JavaScript / TypeScript in Node.js
Yes
Yes
JavaScript / TypeScript in a browser
Yes
No
Rust with Tokio
No
Yes
Install
With npm:
With pnpm:
With Yarn:
Install the SDK from crates.io and enable Tokio's runtime and macros:
The triton-sdk crate is a drop in relacement for the solana-client crate.
Examples
Runnable examples are available for JavaScript / TypeScript and Rust.
Quickstart with gRPC
Use gRPC for backend applications. Set a JSON-RPC URL and an account-sync stream URL for the same cluster. They may be the same URL if your endpoint serves both APIs.
Run this example in Node.js:
Save this example as src/main.rs and run it with cargo run:
Browser with WebSocket (JavaScript / TypeScript)
The package automatically selects its browser entry when used by a browser build tool. The browser entry does not load the Node.js gRPC code. Requesting gRPC in a browser throws during Connection construction.
Choose RPC-only or account-sync mode
Native web3.js connections
Omit accountSync to get an actual @solana/web3.js Connection. Its prototype and methods come from web3.js. The SDK does not create an account-sync core, transport, hydration plugin, or parse cache.
An ordinary web3.js config object also selects native mode. Its fetch, fetchMiddleware, httpHeaders, wsEndpoint, retry settings, and other options go directly to web3.js. An omitted commitment keeps the web3.js default behavior.
Account-sync connections
Pass accountSync: {} to enable buffered account reads with the default settings. Supply options inside that object to configure the transport and buffer. Node supports WebSocket and gRPC; the browser build supports WebSocket.
Account-sync reads default to confirmed when neither the constructor nor accountSync supplies a commitment. The enabled implementation retains its own hydration and parsing behavior. Its hydration requests do not inherit web3.js fetch or header settings.
Choosing a mode
No config, a commitment string, or ordinary web3.js options
Native web3.js
accountSync: undefined
Native web3.js
accountSync: {} or an options object
Account-sync
Only account-sync instances provide addAccounts, removeAccounts, setAccounts, close, and getLastTransportError.
RpcClient::new and the other constructors return RpcClient<Plain>. Its methods use the underlying asynchronous Solana RPC client. Call with_account_sync to get RpcClient<Configured> with buffered raw reads and account controls:
The stream endpoint is required; it is not derived from the RPC URL. Set both to the same URL if your service supports that. The default account read commitment comes from the underlying Solana client. RpcClient::new uses its finalized default; this guide sets confirmed explicitly.
RpcClient::from_inner wraps an existing asynchronous Solana RPC client, including its RPC settings. client.inner() gives direct access to that client and bypasses the account-sync buffer. Other Solana client methods are also available through dereferencing.
Only configured clients provide account controls, account_sync_config(), and close(). Cloned clients share the RPC client and account-sync state. Closing one clone stops account sync for all clones.
How a read works
A supported read can return a buffered value from memory without a new JSON-RPC request. If no suitable value is ready, the SDK uses JSON-RPC and can create a temporary subscription. Both SDKs compare observation slots and stream write order when updating the buffer.
1. An account that is already in the buffer
If the SDK has the value buffered, it returns that value from memory. It does not start a new JSON-RPC request for that read.
2. An account that is not in the buffer
The SDK starts a one-time getMultipleAccounts JSON-RPC request. By default, it also adds a stream subscription for the account. The SDK returns the first suitable answer from the buffer or JSON-RPC.
If JSON-RPC confirms that the account is absent, the result is null. The SDK stores the slot where that absence was observed.
If neither source can produce a suitable answer before missTimeoutMs, the read fails.
3. Startup
The SDK opens the stream and asks JSON-RPC for the current value of every tracked account specified in the initialAccounts field. This prevents the SDK from waiting for the next account change before it knows the starting state.
Stream updates may arrive while the JSON-RPC starting values are loading. The SDK compares their slots and write order and gives you the newest value.
4. When the stream disconnects
The SDK starts JSON-RPC polling for tracked accounts and tries to reconnect in the background. After reconnecting, it sends the complete account set again and gets a fresh JSON-RPC view before it stops polling.
Reads can still succeed during this time. They may be served from a buffered value or a JSON-RPC request.
Startup: Configuration and account-set changes do not start a stream by themselves. The first supported read at a commitment starts that commitment's background tasks. After a stream session opens, the SDK fetches initial values for tracked accounts through RPC. Failed initial reads can be retried on subscription_refresh ticks.
Cache miss: The SDK waits for a complete buffered result while making the normal single-account or multiple-account RPC call. A ready buffered result takes priority. Otherwise, the RPC result can finish the read, including with an error. Successful RPC observations can populate the buffer for tracked accounts.
Wait limit: cache_miss_wait limits the wait for the buffer. If that wait expires, the read continues waiting for RPC. It is not a deadline for the whole read; configure the underlying RPC client's timeout separately.
Disconnect or account-set change: The SDK clears cached entries for the affected commitment and reconnects with the desired account set. Reads use RPC as needed. It does not periodically poll all tracked accounts during a stream outage. After a new session opens, it loads initial values again.
Read one account
Value only
The result is AccountInfo<Buffer> | null.
Account fields
For an account that exists, the result is same as web3.js response which contains:
lamports
Account balance. One SOL is 1,000,000,000 lamports.
owner
PublicKey of the program that owns the account.
executable
Whether the account contains an executable program.
rentEpoch
Epoch when the account will next owe rent.
data
Raw account bytes as a Node.js Buffer.
space
Full account data size in bytes at runtime.
The current web3.js AccountInfo TypeScript type does not declare space, but the Triton SDK includes it at runtime to match current RPC responses.
The result is Account. A missing account returns an AccountNotFound client error. Use the context method when absence is an expected result:
The response contains Option<Account> and a context slot. get_account_data(&address) returns only the Vec<u8> and also fails if the account is missing.
lamports
Account balance as u64. One SOL is 1,000,000,000 lamports.
owner
Pubkey of the program that owns the account.
executable
Whether the account contains an executable program.
rent_epoch
Rent epoch as u64.
data
Raw account bytes as Vec<u8>. Use data.len() for their size.
Rust's Account has no separate space field.
Read several accounts
Results stay in input order. Duplicate addresses produce duplicate result positions. A missing account occupies its own position as null in JavaScript or None in Rust.
To receive a shared context:
The SDK sends unresolved accounts in one JSON-RPC request when possible. Large background hydration sets are split into groups of 100 accounts.
To receive a shared context:
The buffered path accepts 1–100 addresses per call. Empty input and batches larger than 100 go directly to the underlying RPC method, with its usual validation and errors. Rust does not split those calls into smaller batches. This per-call limit is separate from the number of accounts you can subscribe to.
What the context slot means
An RPC observation uses the bank context slot returned by JSON-RPC. A streamed observation uses the slot of the account update. An unchanged account can therefore have an older slot than the current bank. A buffered context slot is not a full bank-progress marker.
For a buffered multiple-account result, the shared context is the lowest observation slot in that result. It does not mean that all accounts came from the same bank snapshot.
JavaScript account read options
The following options apply to the JavaScript account-sync methods. Rust's buffered methods return raw accounts and accept no data-slice or minimum-slot options. Rust calls such as get_account_with_config use the underlying Solana RPC client directly.
Read parsed accounts
Raw account data is bytes. Some common Solana programs have known data layouts. A parsed account replaces those bytes with a JavaScript object that is easier to read.
Read one parsed account:
Read several parsed accounts:
Supporting accounts and parse failures
The parser must support the account's owner program. Some parsers also need another account. For example, parsing an SPL token account needs its mint account to calculate display amounts.
The connection automatically fetches a required mint through its own getAccountInfo method. It keeps these supporting values in a small cache. The default cache holds up to 256 entries for five minutes. Concurrent parses that need the same uncached mint share one fetch.
In account-sync mode, parse failures and missing supporting data reject the read; they do not return raw fallback data. Use raw account methods when you need bytes. dataSlice is intended for raw reads; the parsed methods parse the full buffered account.
Read part of account data
Use dataSlice when you need only a range of raw account bytes:
This means “start at byte 8 and return at most 32 bytes.”
Both offset and length must be non-negative safe integers. A zero length returns an empty Buffer. An offset past the end also returns an empty Buffer. A range that reaches past the end stops at the end.
The slice changes only the returned data. The runtime space field still contains the full size of the account data.
The same option works with single-account and multiple-account methods.
Require a minimum slot
Use minContextSlot when you must not accept an observation older than a known slot:
minContextSlot must be a non-negative safe integer.
On a cache miss, the SDK sends this value to Solana JSON-RPC. If the RPC node has not reached the requested slot, it commonly returns error code -32016. The SDK also waits for a suitable buffered observation until missTimeoutMs.
For unchanged accounts, the last streamed update can be older than the requested slot even when the bank has advanced. See what the context slot means.
Choose a commitment
Both SDKs support processed, confirmed, and finalized. Each commitment uses separate account state and its own stream.
Other web3.js commitment names, such as max, are rejected for account-sync reads.
Set the default for the connection:
You can also set the commitment in the connection config while enabling account sync:
accountSync.commitment wins when both forms are present. With neither form, the default is confirmed.
Override it for one read:
A different commitment has separate account state. The SDK therefore opens a separate stream and buffer for it. An unused non-default commitment stream is closed after its pinned and temporary accounts are gone.
Set the default on the RPC client:
Override it for a read with a context method:
AccountSyncConfig has no commitment field. All commitment streams share the configured pinned account set. Temporary subscriptions belong to the commitment where the read occurred. A stream with no desired accounts waits for new accounts; its background task remains until the client closes.
Choose which accounts stay subscribed
There are two kinds of tracked accounts.
Pinned accounts
Pinned accounts stay subscribed until you remove them or close the connection.
Configure pinned accounts and add more later. Duplicate addresses are ignored.
Pin more accounts later:
client.account_sync_config().pinned_accounts returns a snapshot of the current pinned set. Editing that returned snapshot does not change the running client. The first supported read starts stream work.
Temporary accounts
By default, reading an untracked account creates a temporary subscription. Further reads renew it. Both SDKs use a default idle lifetime of 60 seconds.
Set the idle lifetime with dynamicSubscriptionTtlMs.
Disable this for default-commitment cache misses:
The read still makes a one-time JSON-RPC request. A read at a commitment other than the connection default still needs its own commitment stream and may track the account there.
Set the idle lifetime with dynamic_subscription_lifetime, a Duration. Expired subscriptions are removed on subscription_refresh ticks. Disable automatic subscriptions at every commitment with:
Unpinned reads still use JSON-RPC. They do not retain account state in the buffer when automatic subscriptions are disabled.
Remove or replace accounts
This removes pinned or temporary ownership and clears the cached account state immediately. A read already in progress may finish through its one-time JSON-RPC request.
A later read can create a new temporary subscription. If the account should remain subscribed, call addAccounts before reading it again.
Removing an address that is not tracked does not fail. It also clears any supporting parsed-account cache entry for that address.
Replace the full account set
This makes the given list the complete pinned set for that commitment. It also clears all temporary accounts for that commitment.
Use a specific commitment
All three account control methods accept an optional commitment:
Without this argument, they use the configured account-sync commitment you set during Connection creation.
remove_accounts removes pins. replace_accounts replaces the complete pinned set. These methods have no commitment argument: changes apply to every existing commitment stream and to streams started later.
Neither method clears an existing temporary subscription. That subscription remains until its idle lifetime expires, and reads can renew it. Removing an unknown pin succeeds. To control subscriptions only through the pinned set, set automatic_subscriptions: false when creating the client.
A changed desired account set restarts the affected stream and clears its cached entries. A read already in progress can still finish through RPC. A rapid remove-and-re-add can accept an earlier RPC result if it passes the cache's slot checks.
Complete lifecycle example
These examples add, read, remove, re-add, and replace pinned accounts, then close the client. Automatic subscriptions are disabled to keep the example's account set controlled by explicit calls.
Neither SDK sets a fixed subscription count limit. Your endpoint, memory, CPU, network, and the account-sync service can still have practical limits. Test the size you plan to use.
Configuration reference
All account-sync settings belong under accountSync:
Common settings
transport
"ws" | "grpc"
"ws"
Stream type. Browsers allow only "ws".
subscriptionEndpoint
string
Derived from endpoint
Optional, overrides only the account-sync stream endpoint. JSON-RPC still uses endpoint.
commitment
"processed" | "confirmed" | "finalized"
"confirmed"
Default commitment for buffered reads.
initialAccounts
Array of strings or PublicKey values
[]
Accounts pinned from startup.
autoSubscribeOnMiss
boolean
true
Temporarily subscribe when a default-commitment read misses the buffer.
missTimeoutMs
positive safe integer
5000
Longest wait for a suitable buffered or RPC observation.
rpcPollIntervalMs
positive safe integer
1000
Delay between RPC refreshes while the stream is unavailable.
reconnectInitialDelayMs
positive safe integer
100
First wait before reconnecting a failed stream.
reconnectMaxDelayMs
positive safe integer
5000
Largest reconnect wait. Must be at least the initial delay.
connectTimeoutMs
positive safe integer
10000
Longest wait for one WebSocket connection attempt. Not used by gRPC.
closeTimeoutMs
positive safe integer
5000
Longest wait for account-sync shutdown.
dynamicSubscriptionTtlMs
positive safe integer
60000
Idle lifetime of an account subscription created by a read for an unpinned account.
A safe integer is a whole JavaScript number that can be represented exactly. These time values are milliseconds. Zero, negative values, fractions, NaN, and numbers outside the safe integer range are rejected.
gRPC settings
These settings are valid only with the gRPC transport:
flowControlWindowBytes
16777216
Fixed HTTP/2 receive window used by grpc-js.
maxReceiveMessageLengthBytes
16777216
Largest gRPC message the client accepts.
keepAliveIntervalMs
30000
Time between gRPC keepalive pings.
keepAliveTimeoutMs
10000
Time allowed for a keepalive response.
keepAlivePermitWithoutCalls
true
Allow keepalive pings while no call is active.
Byte and time values must be positive safe integers. The keepalive flag must be a boolean.
Do not add grpc settings while transport is WebSocket. The Node.js constructor rejects that combination. The browser build rejects all gRPC settings.
Separate RPC and stream hosts
Use subscriptionEndpoint if JSON-RPC and account sync have different hosts:
Initial account loading and fallback polling still use the RPC endpoint.
Pass AccountSyncConfig to with_account_sync. Duration settings use std::time::Duration, not integer milliseconds:
endpoint
String
Empty; must be set
Account-sync gRPC URL. RPC uses the URL on the underlying client.
pinned_accounts
HashSet<Pubkey>
Empty
Accounts pinned across commitment streams.
automatic_subscriptions
bool
true
Track unpinned accounts temporarily when read.
cache_miss_wait
Duration
5 seconds
Wait for a buffered result before relying on RPC. Not a whole-read deadline.
subscription_refresh
Duration
1 second
Check temporary subscription expiry and retry missing initial values while a stream session is active. Not an outage polling interval.
reconnect_min_delay
Duration
100 milliseconds
Initial reconnect delay.
reconnect_max_delay
Duration
5 seconds
Maximum reconnect delay; must be at least the minimum.
connect_timeout
Duration
10 seconds
Timeout for establishing a gRPC transport connection.
close_timeout
Duration
5 seconds
Maximum wait for account-sync background tasks to stop.
dynamic_subscription_lifetime
Duration
60 seconds
Idle lifetime for temporary subscriptions.
http2_window_size
u32
16777216 (16 MiB)
Initial HTTP/2 connection and stream receive windows.
max_decoded_message_size
usize
16777216 (16 MiB)
Largest decoded gRPC message accepted.
keepalive_interval
Duration
30 seconds
Interval between HTTP/2 keepalive pings.
keepalive_timeout
Duration
10 seconds
Time allowed for a keepalive response.
keepalive_while_idle
bool
true
Allow keepalive pings without an active stream.
All durations and byte limits must be nonzero. with_account_sync validates the configuration and returns ConfigError on invalid input. There is no transport selector or separate grpc settings object. The Rust client accepts gzip and Zstd stream compression.
RPC commitment and request timeout belong to the underlying client. For example, use RpcClient::new_with_timeout_and_commitment before with_account_sync to set both. account_sync_config() returns an owned snapshot of the configuration, including the current pins.
Errors and recovery
Missing account versus failed read
These cases are different:
null: JSON-RPC successfully observed that the account did not exist.rejected promise: the SDK could not complete the read, the input was invalid, or JSON-RPC returned an error.
Read timeout
Context and multiple-account methods can reject with AccountSyncReadTimeoutError:
getAccountInfo follows web3.js error wording and wraps failures in a new Error that includes the account address. Because of that wrapping, catch AccountSyncReadTimeoutError directly only when using a context or multiple-account method.
JSON-RPC errors
Context and multiple-account methods keep web3.js SolanaJSONRPCError instances. For example, a node that has not reached minContextSlot commonly returns code -32016.
Again, getAccountInfo wraps the original failure, so it does not return the original SolanaJSONRPCError instance to your catch block.
Background errors
Stream, polling, hydration, and reconnect work happens in the background. Read the newest saved background error with:
Despite its method name, this can contain more than a transport error. It can also contain an RPC polling, starting-state, or recovery error.
The SDK keeps the last error even after recovery. A non-null result does not by itself prove that the connection is currently broken. Use it for logs and health details, not as the only reason to stop serving reads.
Connection and shutdown timeouts
A WebSocket attempt that exceeds connectTimeoutMs is reported as an error named AccountSyncConnectTimeoutError. Polling and reconnect work can continue.
If shutdown exceeds closeTimeoutMs, close() rejects with an error named AccountSyncCloseTimeoutError. The connection still remains closed.
After close(), reads and most account-set changes reject with account-sync connection is closed. Calling close() again is safe and shares the same shutdown result.
Missing account versus failed read
get_account_with_commitment returns Ok(Response { value: None, .. }) when the account is absent. Multiple-account results contain None at missing positions. get_account and get_account_data instead return an AccountNotFound client error. RPC failures remain errors, not absent accounts.
Error types
with_account_sync
ConfigError
Invalid endpoint, duration, byte limit, or reconnect range.
Supported account reads
ClientError
Solana RPC client error, including missing accounts for value-only reads.
Account controls and close
AccountSyncError
Account-sync lifecycle or background-task failure.
Stream failures trigger reconnection and reads can use RPC. There is no public last-transport-error method. A successful account read does not prove that the stream is connected.
Read wait and shutdown
Rust has no AccountSyncReadTimeoutError. After cache_miss_wait expires, the read waits for the RPC result under the RPC client's timeout. An RPC error can also finish the read before the buffer wait expires.
close().await cancels account-sync work and waits for its background tasks. It returns AccountSyncError::CloseTimeout if that wait exceeds close_timeout. Subsequent close() calls return successfully. Account controls then return AccountSyncError::Closed, but account reads continue through the underlying RPC client. This applies to all clones of the configured client.
Production guidance
Always close unused connections
Each client can own streams, timers, retries, and pending reads. Use try/finally in JavaScript. In Rust, save the operation result, await close(), then propagate errors, as shown in the lifecycle example. In long-running applications, reuse a client instead of making one per read.
Plan for both the stream and JSON-RPC
JSON-RPC is not only an emergency fallback. It supplies starting state and resolves new account reads. Make sure the endpoint allows the expected RPC load, especially when adding many accounts at once.
Watch memory and network use
The full latest data for tracked accounts is kept in application memory. Large accounts and large account sets increase memory, RPC, and stream traffic. There is no client-side fixed count limit.
Measure with realistic account sizes and update rates. Do not raise gRPC window or message limits without checking memory use.
Large gRPC streams
For large streams, current project guidance is:
use a host with at least 5 Gbps of internet capacity;
keep round-trip time to the Triton endpoint at or below 50 ms;
use the default fixed 16 MiB window settings as a starting point;
avoid short-lived function workers for a continuous stream; and
test the host and connection under expected load before production use.
The JavaScript grpc-js transport uses a fixed HTTP/2 flow-control window and does not support Zstd stream compression. The Rust SDK sets initial connection and stream windows through http2_window_size and accepts gzip and Zstd compression.
Delivery limits
The backend Triton service closes a client session if that client cannot consume updates and its server-side queue fills. Both SDKs reconnect and can serve reads through RPC. JavaScript also polls tracked accounts during the outage.
Observe errors without causing false alarms
Record read errors, successful read rates, response times, and application health signals. JavaScript also exposes getLastTransportError() for background errors. Rust has no equivalent public accessor. Successful reads can come from RPC, so they do not establish stream health.
Troubleshooting
My result is null
JSON-RPC confirmed that the account was missing at the returned context slot. Check the public key and selected commitment. A timeout or network failure is not returned as null.
My read timed out
The stream and JSON-RPC did not provide a suitable observation within missTimeoutMs. Check the endpoint, token, account-sync access, RPC health, and minContextSlot. Increase the timeout only when the expected operation can reasonably need more time.
The browser says gRPC is unsupported
Use AccountSyncTransports.WS. The JavaScript SDK supports gRPC only in Node.js.
The WebSocket does not connect
Check that:
the endpoint starts with
http,https,ws, orwssas appropriate;the token is present when required;
the provider exposes the account-sync WebSocket service;
a proxy allows WebSocket upgrades; and
browser origin rules allow the request.
Call getLastTransportError() for the latest saved background error. The SDK may still be serving reads through JSON-RPC polling.
getLastTransportError() still returns an error after recovery
This is expected. The SDK keeps the latest background error and does not clear it after recovery.
My Node.js process does not exit
Call and await connection.close(). A connection owns background resources that can keep work active.
My minimum slot has not been reached
The RPC node may return code -32016, or the SDK may time out while waiting. Use a reachable slot, a suitable commitment, and enough missTimeoutMs for the cluster to progress. Remember that cached stream state uses the account update slot.
Removing an account did not keep it removed
A later read can create a new temporary subscription. If you want the account to remain absent from the tracked set, do not read it through this connection, or set autoSubscribeOnMiss: false for default-commitment reads.
I configured pinned accounts, but no stream started
Configuration and pin changes do not start background tasks. Make a supported account read inside a Tokio runtime. That starts the stream work for its commitment.
Reads work even though the stream is unavailable
The client can use RPC. Check that AccountSyncConfig.endpoint selects the account-sync service on the same cluster as the RPC URL. Use an HTTP or HTTPS URL with at most one token path segment. There is no public last-transport-error accessor.
My read takes longer than cache_miss_wait
This setting limits waiting for the buffer, not the whole read. Set the underlying RPC timeout with new_with_timeout_and_commitment or an existing client passed to from_inner.
Removing a pin did not unsubscribe the account
An existing temporary subscription remains active until it expires, and reads renew it. replace_accounts also preserves temporary subscriptions. Disable automatic subscriptions at construction if you need the pinned set to be the complete subscription set.
Reads still work after close()
Closing stops account sync for all client clones. Account reads then use RPC. Account controls fail with AccountSyncError::Closed.
My configured read still uses RPC
Only the five methods in the Rust public API summary below use the buffer. Other methods, including get_account_with_config, go to the underlying Solana client. Multiple-account calls with more than 100 keys also bypass the buffer.
Advanced account encoding (JavaScript / TypeScript)
Most users can use getAccountInfo and getParsedAccountInfo and skip this section. These helpers are useful when you already have raw account values and need Solana RPC-style encoding.
Encoding runs in a bundled WebAssembly (WASM) module that works in Node.js and browsers. The module loads when an encoding helper first needs it. All callers share the same loading operation.
The encoding helpers do not open network connections or fetch supporting accounts.
Supported encodings
binary
A base58 string. This is the legacy encoding name.
base58
A [data, "base58"] tuple.
base64
A [data, "base64"] tuple.
base64+zstd
A tuple containing base64-encoded, zstd-compressed data and "base64+zstd".
jsonParsed
A ParsedAccountData object, or an error if parsing fails.
Convert raw account data
convertAccountData converts between raw encodings. It does not accept jsonParsed as either the source or destination encoding.
Encode a web3.js account
encodeAccount returns a JSON-compatible UiAccount. Its fields are lamports, data, owner, executable, rentEpoch, and optional space.
An AccountEncodingInput accepts:
pubkey: a string orPublicKey.owner: a string orPublicKey.lamports: an unsigned 64-bit value as a number, bigint, or decimal string.executable: a boolean.rentEpoch: an unsigned 64-bit value as a number, bigint, or decimal string.data: aUint8Array,Buffer, or base64 string.
Unsafe numeric inputs, negative values, values outside the unsigned 64-bit range, invalid addresses, and invalid base64 strings are rejected.
For raw encodings, the optional dataSlice selects a byte range before encoding. The space field still reports the full account data size. Slicing does not apply to jsonParsed.
Supply parse context
Some account parsers need supporting data. For example, parsing an SPL token account requires its mint account data.
Load that data before calling the helper and supply it through parseContext:
parseJsonParsed parses once with the supplied context. It returns ParsedAccountData or rejects with an error. It does not fetch missing data, retry parsing, or return raw data after a failure.
encodeAccount(input, "jsonParsed", options) accepts the same parse context and follows the same rules. It returns a full UiAccount containing the parsed data.
unixTimestamp supplies the time for parsers whose output depends on it. A top-level options.unixTimestamp overrides parseContext.unixTimestamp.
Account-sync connection methods handle supporting reads separately. They can load a missing mint and retry parsing once. A parse or context fetch failure rejects the read. For getMultipleParsedAccounts, one failure rejects the entire call; missing accounts return null.
Without accountSync, Connection uses the upstream web3.js behavior.
Encoding error classes
UnsupportedEncodingError
The requested encoding or conversion is unsupported.
InvalidAccountDataError
An account field, encoded value, or option is invalid.
MissingParseContextError
Parsing requires supporting data that was not supplied.
WasmParserError
The account parser failed, including for unsupported programs or malformed account data, or WASM returned invalid JSON.
ContextFetchError
An account-sync connection could not load supporting parse data.
These errors can include the account address, owner, encoding, missing account, context kind, or original cause when that information is available.
Lower-level helpers
loadAccountEncodingWasm()loads and returns the bundled encoder.bufferedAccountToEncodingInput()converts an internal buffered state. Most application code will not have this internal type.accountInfoToEncodingInput()converts a raw web3.js account result.toWeb3JsParsedAccountInfo()converts a JSON-compatible parsedUiAccountinto the web3.js account shape. It rejects raw encoded data.
Compatibility and migration
Without accountSync, the SDK returns a native web3.js Connection. With account sync enabled, it returns an implementation that extends the web3.js Connection class. The package also re-exports web3.js values and types.
These methods are replaced with account-sync-backed versions:
getAccountInfogetAccountInfoAndContextgetParsedAccountInfogetMultipleAccountsInfogetMultipleAccountsInfoAndContextgetMultipleParsedAccounts
Other inherited methods, such as getSlot, continue to use JSON-RPC normally.
This often makes migration small:
Then add accountSync configuration and call close() when you are finished.
The six method signatures and normal account result shapes match web3.js. The behaviour differs in important ways described in this guide: local buffering, temporary subscriptions, fallback polling, and commitment-specific streams.
The SDK wraps solana_client::nonblocking::rpc_client::RpcClient. Use triton_sdk::RpcClient and enable account sync with with_account_sync. Use the SDK's re-exported Pubkey, Account, and CommitmentConfig to match its Solana dependency versions.
To retain an existing Solana client's RPC configuration, wrap it with RpcClient::from_inner(existing_client) before enabling account sync. This requires a compatible Solana client version.
The five raw account methods listed below use the buffer when possible. Other Solana methods remain available through dereferencing and use RPC, including configured and parsed RPC reads. Calling client.inner() explicitly bypasses buffered reads as well.
with_account_sync returns RpcClient<Configured>, so update explicit type annotations that previously used plain RpcClient. The wrapper is not the same concrete type as Solana's client. It does not provide a blocking account-sync client or the JavaScript encoding helpers.
Public API summary
Main values and classes
Connection: web3.js-compatible connection with buffered account reads.AccountSyncTransports: containsWSandGRPCtransport choices.PublicKeyand other web3.js exports: re-exported from@solana/web3.js.AccountSyncReadTimeoutError: typed timeout from context and multiple-account reads.
Connection methods added or changed
Encoding values and classes
AccountParseContextCacheContextFetchErrorInvalidAccountDataErrorMissingParseContextErrorUnsupportedEncodingErrorWasmParserErroraccountInfoToEncodingInputbufferedAccountToEncodingInputconvertAccountDataencodeAccountisBase64ZstdEncodingSupportedloadAccountEncodingWasmparseJsonParsedtoWeb3JsParsedAccountInfo
Exported account-sync types
Both builds export:
AccountSyncCommitmentAccountSyncOptionsAccountSyncReadTimeoutErrorOptions
The Node.js build also exports:
GrpcTransportOptionsNodeAccountSyncConnectionConfigNodeSubscriptionTransport
The browser build instead exports:
BrowserAccountSyncConnectionConfigBrowserSubscriptionTransport
Exported encoding types
AccountDataEncodingAccountEncodingInputAccountEncodingOptionsAccountParseContextAccountParseContextAccountAccountParseContextCacheOptionsAccountParseContextFetcherAccountParseContextMintConvertAccountDataOptionsEncodedAccountDataUiAccount
Main exports
RpcClient, also available attriton_sdk::nonblocking::rpc_client::RpcClient.PlainandConfigured: client mode types.AccountSyncConfig,ConfigError, andAccountSyncError.Account,Pubkey,CommitmentConfig,CommitmentLevel,ClientError, and the Solanaresponsemodule.
Buffered methods on RpcClient<Configured>
get_account(&pubkey)
Account; absence is an error.
get_account_data(&pubkey)
Vec<u8>; absence is an error.
get_account_with_commitment(&pubkey, commitment)
Response<Option<Account>>.
get_multiple_accounts(&pubkeys)
Vec<Option<Account>>.
get_multiple_accounts_with_commitment(&pubkeys, commitment)
Response<Vec<Option<Account>>>.
All five are async and return a Result with the Solana ClientError type. Multiple-account methods use the buffer only for 1–100 input addresses.
Constructors and account-sync controls
Account controls accept an iterator of Pubkey values and return Result<(), AccountSyncError>. They have no commitment argument. Configuration inspection is synchronous and returns a snapshot.
Last updated
Was this helpful?