Fetching accounts

Read and decode onchain account data

Reading data from Solana usually means fetching one or more accounts and decoding their data into something your application can use. This guide walks through the helpers Kit provides at each step, from the raw RPC call all the way to typed account objects.

Fetch raw account data

The fetchEncodedAccount helper wraps the getAccountInfo RPC method and returns a MaybeEncodedAccount. The result always has the same shape regardless of whether the account exists, which makes it easier to handle than the raw RPC response.

import { ,  } from '@solana/kit';
const  = ('GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ');
const  = await (., );

When the account exists, you get back its address, lamports, owner program, and a data field containing the raw bytes as a Uint8Array. When it does not, the same object is returned with exists: false and just the address.

Handle missing accounts

A MaybeEncodedAccount is a discriminated union: TypeScript narrows the type once you check account.exists. You can branch on that flag, or use assertAccountExists when you would rather throw if the account is not present.

import { , ,  } from '@solana/kit';
const  = ('GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ');
const  = await (., );
();
 
// `account` is now `EncodedAccount`, with `data` typed as `Uint8Array`.
. satisfies ;

Asserting up front lets the rest of your function rely on a fully populated account, which is convenient for scripts and tests. In application code, branching on account.exists is usually a better fit so you can show a sensible empty state. When working with several accounts at once, assertAccountsExist(accounts) performs the same check across an entire array in one call.

Fetch multiple accounts

When you need several accounts at once, fetchEncodedAccounts batches them into a single getMultipleAccounts RPC request and returns a parallel array of MaybeEncodedAccount values.

import { ,  } from '@solana/kit';
const  = await (., [
    ('GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ'),
    ('Ay1zdJ3VDbhrAtkRiqBUJgKLHYbgFZN5BXk7svtMowif'),
]);

Each returned account already carries its own address, so you do not need to zip arrays together to figure out which result belongs to which input. The same exists flag works on every entry, and assertAccountsExist(accounts) is the multi-account counterpart of assertAccountExists.

Decode accounts manually

Once you have an encoded account, the decodeAccount helper turns it into a typed Account (or MaybeAccount) by running its data through any Decoder. The Codecs guide covers how to build these decoders.

import { , ,  } from '@solana/kit';
import {
    ,
    ,
    ,
    ,
    ,
    ,
} from '@solana/kit';
 
type  = { : string; : number };
const : <> = ([
    ['name', ((), ())],
    ['age', ()],
]);
 
const  = ('GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ');
const  = await (., );
const  = (, );

decodeAccount preserves the MaybeAccount shape: a missing account stays missing, and a present account gets its data swapped from raw bytes to your decoded type. This means you can keep narrowing with account.exists exactly like before.

Decode using program helpers

For most popular Solana programs you will not need to write a decoder yourself. The @solana-program/* packages include generated helpers that handle decoding for every account they define, exposed as decodeX, fetchX, and fetchMaybeX functions where X is the account name.

import {  } from '@solana/kit';
import {  } from '@solana-program/token';
const  = await (., ('So11111111111111111111111111111111111111112'));
..; // typed as `number`

Each program package follows the same naming convention: fetchMint and fetchMaybeMint for the Mint account, fetchToken and fetchMaybeToken for the Token account, and so on. The standalone decodeMint and decodeToken helpers work on encoded accounts you have already fetched. If you would rather access these through a typed client.token.accounts.* namespace, see the Using program plugins guide.

Subscribe to account changes

If you need live updates rather than a one-off read, pair an account fetch with an account subscription. The subscription notifies you whenever the account's data changes onchain, and the same address is used in both calls.

import {  } from '@solana/kit';
const  = ('GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ');
const  = await .
    .(, { : 'confirmed' })
    .({ : .(60_000) });
 
for await (const  of ) {
    .('Account changed:', .);
}

See the RPC subscriptions guide for more on cancellation, error handling, and the async iterator model.

Choose the right fetch helper

Each helper trades a different amount of structure and convenience. The table below summarizes when each one shines.

HelperReturnsBest for
client.rpc.getAccountInfo(...)raw RPC responseone-off reads with custom encoding
client.rpc.getMultipleAccounts(...)raw RPC responsebulk reads with custom encoding
fetchEncodedAccount(...)MaybeEncodedAccountunified handling of single accounts
fetchEncodedAccounts(...)MaybeEncodedAccount[]unified batch reads
decodeAccount(...)Account<T> / MaybeAccount<T>turning raw bytes into typed data
fetchMint, fetchToken, ...Account<T> / MaybeAccount<T>typed reads for known program accounts
accountNotifications(...)async iterator of account updateslive updates instead of one-off reads

In practice you can pick any of them in isolation, or combine them to match the access pattern you need.

Next steps

  • Codecs — learn how to build custom decoders.
  • RPC requests — explore the rest of the RPC API.
  • Using program plugins — read typed accounts through client.token.accounts.mint.fetch(...) and friends.

On this page