Solana Account Model
On Solana, all data is stored in what are called "accounts." You can think of data on Solana as a public database with a single "Accounts" table, where each entry in this table is an "account." Every Solana account shares the same base Account type.
Accounts
Key Points
- Accounts can store up to 10MiB of data, which contains either executable program code or program state.
- Accounts require a rent deposit in lamports (SOL) that's proportional to the amount of data stored, and you can fully recover it when you close the account.
- Every account has a program owner. Only the program that owns an account can change its data or deduct its lamport balance. But anyone can increase the balance.
- Sysvar accounts are special accounts that store network cluster state.
- Program accounts store the executable code of smart contracts.
- Data accounts are created by programs to store and manage program state.
Account
Every account on Solana has a unique 32-byte address, often shown as a base58
encoded string (e.g. 14grJpemFaf88c8tiVb77W7TYg2W3ir6pfkKz3YjhhZ5
).
The relationship between the account and its address works like a key-value pair, where the address is the key to locate the corresponding on-chain data of the account. The account address acts as the "unique ID" for each entry in the "Accounts" table.
Account Address
Most Solana accounts use an Ed25519 public key as their address.
import { Keypair } from "@solana/web3.js";const keypair = Keypair.generate();console.log(`Public Key: ${keypair.publicKey}`);console.log(`Secret Key: ${keypair.secretKey}`);
You must enable the "Enable Custom URL Param" setting on Solana Explorer.
If not enabled, links will default to localhost:8899 instead of the Mirror.ad RPC URL.
While public keys are commonly used as account addresses, Solana also supports a feature called Program Derived Addresses (PDAs). PDAs are special addresses that you can deterministically derive from a program ID and optional inputs (seeds). The details are on the Program Derived Address page.
import { PublicKey } from "@solana/web3.js";const programAddress = new PublicKey("11111111111111111111111111111111");const seeds = [Buffer.from("helloWorld")];const [pda, bump] = await PublicKey.findProgramAddressSync(seeds,programAddress);console.log(`PDA: ${pda}`);console.log(`Bump: ${bump}`);
You must enable the "Enable Custom URL Param" setting on Solana Explorer.
If not enabled, links will default to localhost:8899 instead of the Mirror.ad RPC URL.
Account Type
Accounts have a max size of 10MiB and every account on Solana shares the same base Account type.
Account Type
Every Account on Solana has the following fields:
data
: A byte array that stores arbitrary data for an account. For non-executable accounts, this often stores state that's meant be read from. For program accounts (smart contracts), this contains the executable program code. The data field is commonly called "account data."executable
: This flag shows if an account is a program.lamports
: The account's balance in lamports, the smallest unit of SOL (1 SOL = 1 billion lamports).owner
: The program ID (public key) of the program that owns this account. Only the owner program can change the account's data or deduct its lamports balance.rent_epoch
: A legacy field from when Solana had a mechanism that periodically deducted lamports from accounts. While this field still exists in the Account type, it is no longer used since rent collection was deprecated.
pub struct Account {/// lamports in the accountpub lamports: u64,/// data held in this account#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]pub data: Vec<u8>,/// the program that owns this account. If executable, the program that loads this account.pub owner: Pubkey,/// this account's data contains a loaded program (and is now read-only)pub executable: bool,/// the epoch at which this account will next owe rentpub rent_epoch: Epoch,}
import { Keypair, Connection, LAMPORTS_PER_SOL } from "@solana/web3.js";const keypair = Keypair.generate();console.log(`Public Key: ${keypair.publicKey}`);const connection = new Connection("http://localhost:8899", "confirmed");// Funding an address with SOL automatically creates an accountconst signature = await connection.requestAirdrop(keypair.publicKey,LAMPORTS_PER_SOL);await connection.confirmTransaction(signature, "confirmed");const accountInfo = await connection.getAccountInfo(keypair.publicKey);console.log(accountInfo);
You must enable the "Enable Custom URL Param" setting on Solana Explorer.
If not enabled, links will default to localhost:8899 instead of the Mirror.ad RPC URL.
Rent
To store data on-chain, accounts must also keep a lamport (SOL) balance that's proportional to the amount of data stored on the account (in bytes). This balance is called "rent," but it works more like a deposit because you can recover the full amount when you close an account. You can find the calculation here using these constants.
The term "rent" comes from a deprecated mechanism that regularly deducted lamports from accounts that fell below the rent threshold. This mechanism isn't active anymore.
Program Owner
On Solana, "smart contracts" are called programs. Program ownership is a key part of the Solana Account Model. Every account has a designated program as its owner. Only the owner program can:
- Change the account's
data
field - Deduct lamports from the account's balance
System Program
By default, all new accounts are owned to the System Program. The System Program does a few key things:
- New Account Creation: Only the System Program can create new accounts.
- Space Allocation: Sets the byte capacity for the data field of each account.
- Transfer / Assign Program Ownership: Once the System Program creates an account, it can reassign the designated program owner to a different program account. That's how custom programs take ownership of new accounts created by the System Program.
All "wallet" accounts on Solana are just accounts owned by the System Program. The lamport balance in these accounts shows the amount of SOL owned by the wallet. Only accounts owned by the System Program can pay transaction fees.
System Account
Sysvar Accounts
Sysvar accounts are special accounts at predefined addresses that provide access to cluster state data. These accounts update dynamically with data about the network cluster. You can find the full list of Sysvar Accounts here.
import { Connection, SYSVAR_CLOCK_PUBKEY } from "@solana/web3.js";const connection = new Connection("http://localhost:8899", "confirmed");const accountInfo = await connection.getAccountInfo(SYSVAR_CLOCK_PUBKEY);console.log(JSON.stringify(accountInfo, null, 2));
You must enable the "Enable Custom URL Param" setting on Solana Explorer.
If not enabled, links will default to localhost:8899 instead of the Mirror.ad RPC URL.
Program Account
Deploying a Solana program creates an executable program account. The program account stores the executable code of the program.
Program accounts are owned by a Loader Program.
Program Account
For simplicity, you can treat the program account as the program itself. When you invoke a program's instructions, you specify the program account's address (commonly called the "Program ID").
import { Connection, PublicKey } from "@solana/web3.js";const connection = new Connection("http://localhost:8899", "confirmed");const accountInfo = await connection.getAccountInfo(new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"));console.log(accountInfo);
You must enable the "Enable Custom URL Param" setting on Solana Explorer.
If not enabled, links will default to localhost:8899 instead of the Mirror.ad RPC URL.
When you deploy a Solana program, it's stored in a program account. Program accounts are owned by a Loader Program. There are several versions of the loader, but all except loader-v3 store the executable code directly in the program account. Loader-v3 stores the executable code in a separate "program data account" and the program account just points to it. When you deploy a new program, the Solana CLI uses the latest loader version by default.
Buffer Account
Loader-v3 has a special account type for temporarily staging the upload of a program during deployment or redeployment/upgrades. In loader-v4, there are still buffers, but they're just normal program accounts.
Program Data Account
Loader-v3 works differently from all other BPF Loader programs. The program
account only contains the address of a program data account, which stores the
actual executable code:
Program Data Account
Don't confuse these program data accounts with the data accounts of programs (see below).
Data Account
On Solana, the executable code of a program is stored in a different account than the program's state. This is like how operating systems typically have separate files for programs and their data.
To maintain state, programs define instructions to create separate accounts that they own. Each of these accounts has its own unique address and can store any arbitrary data defined by the program.
Data Account
Note that only the System Program can create new accounts. Once the System Program creates an account, it can then transfer or assign ownership of the new account to another program.
In other words, creating a data account for a custom program takes two steps:
- Invoke the System Program to create an account, then transfer ownership to the custom program
- Invoke the custom program, which now owns the account, to initialize the account data as defined by the program's instruction
This account creation process is often abstracted as a single step, but it's helpful to understand the underlying process.
import {Connection,Keypair,sendAndConfirmTransaction,SystemProgram,Transaction,LAMPORTS_PER_SOL} from "@solana/web3.js";import {createInitializeMintInstruction,TOKEN_2022_PROGRAM_ID,MINT_SIZE,getMinimumBalanceForRentExemptMint} from "@solana/spl-token";// Create connection to local validatorconst connection = new Connection("http://localhost:8899", "confirmed");const recentBlockhash = await connection.getLatestBlockhash();// Generate a new keypair for the fee payerconst feePayer = Keypair.generate();// Airdrop 1 SOL to fee payerconst airdropSignature = await connection.requestAirdrop(feePayer.publicKey,LAMPORTS_PER_SOL);await connection.confirmTransaction({blockhash: recentBlockhash.blockhash,lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,signature: airdropSignature});// Generate keypair to use as address of mintconst mint = Keypair.generate();const createAccountInstruction = SystemProgram.createAccount({fromPubkey: feePayer.publicKey,newAccountPubkey: mint.publicKey,space: MINT_SIZE,lamports: await getMinimumBalanceForRentExemptMint(connection),programId: TOKEN_2022_PROGRAM_ID});const initializeMintInstruction = createInitializeMintInstruction(mint.publicKey, // mint pubkey9, // decimalsfeePayer.publicKey, // mint authorityfeePayer.publicKey, // freeze authorityTOKEN_2022_PROGRAM_ID);const transaction = new Transaction().add(createAccountInstruction,initializeMintInstruction);const transactionSignature = await sendAndConfirmTransaction(connection,transaction,[feePayer, mint] // Signers);console.log("Mint Address: ", mint.publicKey.toBase58());console.log("Transaction Signature: ", transactionSignature);const accountInfo = await connection.getAccountInfo(mint.publicKey);console.log(accountInfo);
You must enable the "Enable Custom URL Param" setting on Solana Explorer.
If not enabled, links will default to localhost:8899 instead of the Mirror.ad RPC URL.
Is this page helpful?