Account Subscription and Updates
Learn how to subscribe to account updates and efficiently track on-chain state changes using Laserstream.
When building applications that need to respond to on-chain changes, polling RPC endpoints for account updates is both inefficient and slow. Account subscriptions solve this by delivering real-time updates about account state changes directly to your application.
This guide covers everything you need to know about account subscriptions: what they are, how they work, and how to optimize them for your specific use case.
The account model context
Skip this section if you’re familiar with Solana accounts and their structure.
Solana uses an account-based model where every piece of data lives in an account - a container that holds both data and metadata. Each account has:
- Data: The actual bytes storing program state, token balances, or other information
- Owner: The program that controls this account and can modify its data
- Lamports: The account’s SOL balance for rent exemption
- Executable: Whether this account contains program code
Programs are stateless - they don’t store data internally. Instead, they create and manage separate accounts to store their state. When you interact with a program, you pass in the accounts it should read from or write to.
This design makes account subscriptions powerful: you can watch for changes to specific accounts, all accounts owned by a program, or accounts matching certain criteria.
Basic account subscription
Let’s start with a simple example that subscribes to changes in token accounts. This script will notify you whenever token balances change:
When you run this basic subscription, you’ll see real-time account updates streaming to your console:
What just happened? Our subscription worked perfectly! We asked Laserstream to notify us about token account changes, and it delivered an update about account BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF
.
This account has:
- 2,039,280 lamports (~0.002 SOL balance - this is the rent-exempt amount for this token account)
- Owner program
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
(this is the SPL Token program) - Transaction signature
5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD
showing which specific transaction caused this account to change - Slot 352366983 indicating when this update occurred on the blockchain
- Data field containing 165 bytes of account data encoded as base58
Understanding account filtering with datasize
The data field is crucial - it contains the actual token account structure. Let’s use this understanding for smart account filtering.
Why use datasize filtering?
To understand why we need filtering, let’s first understand what token accounts actually are. For every token a wallet holds, there’s a separate account on-chain. If your wallet holds 3 different tokens (USDC, BONK, and SOL), you actually have 1 wallet account (your main SOL account) plus 3 token accounts (one for each token type). Each token account is exactly 165 bytes and stores: which token it holds (mint address), who owns it (your wallet address), and how much of that token it contains (amount).
The Token Program owns millions of accounts on Solana, but not all of them are what we think of as “token accounts” holding user balances. Here’s what happens with and without filtering:
Without filtering - The flood:
This subscribes to ALL accounts owned by the Token Program, which includes:
- Token accounts (165 bytes) - User balances: millions of accounts
- Mint accounts (82 bytes) - Token definitions: hundreds of thousands of accounts
- Multisig accounts (355 bytes) - Shared wallet controls: tens of thousands of accounts
- Associated Token Program accounts (various sizes) - millions of accounts
Result: Your application receives millions of account updates constantly, most of which you don’t care about.
With smart filtering - Surgical precision:
This filters down to only the 165-byte accounts, which are specifically the user token balance accounts - exactly what you want for tracking token transfers, balance changes, and portfolio updates.
The difference:
- Without filtering: Millions of account updates (mint creations, multisig changes, etc.)
- With datasize filtering: Only token balance changes
That’s a significant reduction in noise, focusing only on the accounts that actually represent user token holdings.
Where does 165 bytes come from?
This isn’t magic - it comes from the SPL Token program’s account structure. Looking at the source code, we can see the Account
struct defines exactly 165 bytes:
This fixed size allows us to filter precisely for standard token accounts and exclude:
- Mint accounts (82 bytes)
- Multisig accounts (355 bytes)
- Associated token account program accounts
- Other token-related accounts with different sizes
For calculating account sizes in other programs, check out the Anchor Space Reference - it shows you how much space different data types take (Pubkey = 32 bytes, u64 = 8 bytes, etc.).
Decoding the account structure
Now that we understand why we filtered for 165 bytes, let’s decode what’s inside our example account:
The 165 bytes break down as:
- Bytes 0-31: Mint address (which token this account holds)
- Bytes 32-63: Owner address (who owns this token account)
- Bytes 64-71: Token amount (how many tokens are in the account)
- Bytes 72-164: Additional metadata (delegate, state, close authority, etc.)
This structured approach gives us surgical precision: we only receive updates for standard token accounts, not the noise from other account types.
Combining filters: datasize + memcmp for laser precision
Now that we know the mint address lives at bytes 0-31, we can get even more specific. Let’s say we only want to monitor USDC token accounts. We can combine our datasize
filter with a memcmp
filter to target the exact mint address:
Progressive filtering strategy:
- Owner filter: “Give me accounts owned by Token Program” (millions of accounts)
- Datasize filter: “But only 165-byte standard token accounts” (hundreds of thousands)
- Memcmp filter: “And only those holding USDC” (thousands)
This progression from broad to specific is the key to efficient account monitoring. Each filter narrows down the result set, so you only receive the exact updates you care about.
Important: All filters use AND logic - every condition must be met for an account update to trigger.
Reading USDC account updates: Who, How Much, Where?
Now let’s see what these filtered updates actually contain. Let’s create a USDC-specific monitor that answers the key questions when a token account changes:
- Who owns this token account?
- How much USDC does it now contain?
- Where (which specific account) changed?
- When did this change happen?
- What transaction caused the change?
The raw account updates contain binary data that we need to decode. Since Solana uses base58 encoding for addresses and signatures, we use the bs58.encode()
function to convert binary Buffer objects to readable strings.
When you run this USDC monitor, you’ll see clean, structured output like this:
Each block represents a USDC account that changed state. The first account now holds 1,500 USDC, while the second account has been emptied to 0 USDC. You get the current balance immediately after each transaction, along with which specific account changed and when.
Account subscriptions show you the end result of what happened to each account, not the transaction details. If you need to understand the full transaction context (who sent to whom, fees, etc.), you’d need to fetch the full transaction using the signature shown.
Complete filtering reference
Beyond the basic owner
, datasize
, and memcmp
filters we’ve used, account subscriptions support additional filtering options to further narrow your results:
Specific account filtering
Monitor exact accounts by their public keys:
This approach works well when you know exactly which accounts matter to your application - like monitoring your application’s treasury accounts or specific user accounts.
Combined filtering strategies
The power comes from combining multiple filter types. Here’s the mental model:
- Cast a wide net with
owner
- “Give me all accounts managed by this program” - Filter by structure with
datasize
- “But only accounts of this specific type” - Target specific data with
memcmp
- “And only those containing this specific information” - Monitor known accounts with
account
- “Or just watch these exact accounts I care about”
For example, monitoring high-value USDC accounts:
The key insight is that each filter reduces the volume of updates you receive. Without filtering, you might get overwhelming amounts of account updates. With smart filtering, you get only the updates that matter to your specific use case.
Understanding the bigger picture
Think of account subscriptions as watching a live feed of database changes. Solana’s state is essentially a massive key-value store where each account is an entry. When programs execute, they modify these accounts. Your subscription lets you watch specific entries change in real-time.
The filtering system works like database indexes - you’re not just watching “all changes” but rather “changes to accounts that match these criteria.” This makes it possible to build responsive applications that react immediately to relevant on-chain events without overwhelming your system with irrelevant data.
Applying this pattern to other programs
The approach we’ve learned works for any Solana program. Here’s the general pattern:
- Research the account structure - Check the program’s source code or documentation
- Start with owner filtering - Target the program that manages the accounts
- Apply structural filters - Use account size, data patterns, or other characteristics to narrow down to specific account types
- Add targeted filters - Focus on specific accounts, states, or data values that matter to your application