---
title: Account Types
description:
  Solana account types — program accounts for executable code, data accounts for
  program state, system accounts owned by the System Program, and sysvars for
  cluster-wide state at predefined addresses.
url: /docs/core/accounts/account-types
type: conceptual
prerequisites:
  - /docs/core/accounts
  - /docs/core/accounts/account-structure
related:
  - /docs/core/programs
  - /docs/core/accounts/modification-rules
  - /docs/core/programs/builtin-programs
---

<Callout type="info" title="Summary">
  Program accounts hold executable sBPF code. Data accounts store state, owned
  by programs. System accounts are owned by the System Program. Sysvars provide
  cluster-wide state accessible at predefined addresses.
</Callout>

The [`executable`](/docs/core/accounts/account-structure) field determines an
account's category:

- **[Program accounts](#program-accounts)**: `executable` = `true`. Contains
  executable code.
- **[Data accounts](#data-accounts)**: `executable` = `false`. Stores state or
  user data.

This separation of code from mutable state means a program is deployed once and
can manage any number of data accounts.

## Program accounts

A program account stores executable code. Every program account is owned by a
[loader program](/docs/core/programs/builtin-programs#loader-programs). When a
[program](/docs/core/programs) is deployed, the runtime creates a program
account to hold its bytecode.

![Diagram of a program account, its 4 components and its loader program.](/assets/docs/core/accounts/program-account-simple.svg)

### Program data accounts

Programs deployed using loader-v3 (see
[Loader programs](/docs/core/programs/builtin-programs#loader-programs)) do not
store executable bytecode in their own `data` field. Instead, their `data`
points to a separate **program data account** that contains the program code.
(See the diagram below.)

![A program account with data. The data points to a separate program data account](/assets/docs/core/accounts/program-account-expanded.svg)

<Callout>
  During program deployment or upgrades, buffer accounts are used to temporarily
  stage the upload.
</Callout>

The following example fetches the Token Program account. The `executable` field
is `true`, confirming it is a program account.

<CodeTabs storage="accounts" flags="r">

```ts !! title="Kit"
import { Address, createClient } from "@solana/kit";
import { solanaRpc } from "@solana/kit-plugin-rpc";
import { generatedPayer } from "@solana/kit-plugin-signer";

const client = await createClient()
  .use(generatedPayer())
  .use(
    solanaRpc({
      rpcUrl: "https://api.mainnet.solana.com"
    })
  );

const programId = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address;

const accountInfo = await client.rpc
  .getAccountInfo(programId, { encoding: "base64" })
  .send();
console.log(accountInfo);
```

```ts !! title="Legacy"
import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection(
  "https://api.mainnet.solana.com",
  "confirmed"
);

const programId = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");

const accountInfo = await connection.getAccountInfo(programId);
// !collapse(1:17) collapsed
console.log(
  JSON.stringify(
    accountInfo,
    (key, value) => {
      if (key === "data" && value && value.length > 1) {
        return [
          value[0],
          "...truncated, total bytes: " + value.length + "...",
          value[value.length - 1]
        ];
      }
      return value;
    },
    2
  )
);
```

```rs !! title="Rust"
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::pubkey;

#[tokio::main]
async fn main() -> Result<()> {
    let connection = RpcClient::new_with_commitment(
        "https://api.mainnet.solana.com".to_string(),
        CommitmentConfig::confirmed(),
    );

    let program_id = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");

    let account_info = connection.get_account(&program_id).await?;
    println!("{:#?}", account_info);

    Ok(())
}
```

</CodeTabs>

## Data accounts

Data accounts do not contain executable code. They store program-defined state.

### Program state account

Programs store their state in data accounts. Creating a program state account
involves two steps:

1. Invoke the
   [System Program](/docs/core/programs/builtin-programs#the-system-program) to
   create the account. The System Program transfers ownership to the specified
   program.
2. The owning program initializes the account's `data` field according to its
   [instructions](/docs/core/instructions).

![Diagram of a data account owned by a program account](/assets/docs/core/accounts/data-account.svg)

The following example creates and fetches a Token Mint account owned by the
Token 2022 program.

<CodeTabs storage="accounts" flags="r">

```ts !! title="Kit"
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import { systemProgram } from "@solana-program/system";
import {
  getInitializeMintInstruction,
  getMintSize,
  TOKEN_2022_PROGRAM_ADDRESS,
  fetchMint
} from "@solana-program/token-2022";

const client = await createClient()
  .use(generatedPayer())
  .use(
    solanaRpc({
      rpcUrl: "http://localhost:8899",
      rpcSubscriptionsUrl: "ws://localhost:8900"
    })
  )
  .use(rpcAirdrop())
  .use(airdropPayer(lamports(1_000_000_000n)))
  .use(systemProgram());

// Generate keypair to use as address of mint
const mint = await generateKeyPairSigner();

// Get default mint account size (in bytes), no extensions enabled
const space = BigInt(getMintSize());

// Get minimum balance for rent exemption
const rent = await client.rpc.getMinimumBalanceForRentExemption(space).send();

const transactionSignature = await client.sendTransaction([
  client.system.instructions.createAccount({
    newAccount: mint,
    lamports: rent,
    space,
    programAddress: TOKEN_2022_PROGRAM_ADDRESS
  }),
  getInitializeMintInstruction({
    mint: mint.address,
    decimals: 9,
    mintAuthority: client.payer.address
  })
]);

console.log("Mint Address:", mint.address);
console.log("Transaction Signature:", transactionSignature.context.signature);

const accountInfo = await client.rpc.getAccountInfo(mint.address).send();
console.log(accountInfo);

const mintAccount = await fetchMint(client.rpc, mint.address);
console.log(mintAccount);
```

```ts !! title="Legacy"
import {
  Connection,
  Keypair,
  sendAndConfirmTransaction,
  SystemProgram,
  Transaction,
  LAMPORTS_PER_SOL
} from "@solana/web3.js";
import {
  createInitializeMintInstruction,
  TOKEN_2022_PROGRAM_ID,
  MINT_SIZE,
  getMinimumBalanceForRentExemptMint,
  getMint
} from "@solana/spl-token";

// Create connection to local validator
const connection = new Connection("http://localhost:8899", "confirmed");
const recentBlockhash = await connection.getLatestBlockhash();

// Generate a new keypair for the fee payer
const feePayer = Keypair.generate();

// Airdrop 1 SOL to fee payer
const 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 mint
const 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 pubkey
  9, // decimals
  feePayer.publicKey, // mint authority
  feePayer.publicKey, // freeze authority
  TOKEN_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);

// !collapse(1:16) collapsed
console.log(
  JSON.stringify(
    accountInfo,
    (key, value) => {
      if (key === "data" && value && value.length > 1) {
        return [
          value[0],
          "...truncated, total bytes: " + value.length + "...",
          value[value.length - 1]
        ];
      }
      return value;
    },
    2
  )
);

const mintAccount = await getMint(
  connection,
  mint.publicKey,
  "confirmed",
  TOKEN_2022_PROGRAM_ID
);
console.log(mintAccount);
```

```rs !! title="Rust"
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
    program_pack::Pack,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::create_account;
use spl_token_2022_interface::{
    id as token_2022_program_id, instruction::initialize_mint, state::Mint,
};

#[tokio::main]
async fn main() -> Result<()> {
    // Create connection to local validator
    let client = RpcClient::new_with_commitment(
        String::from("http://localhost:8899"),
        CommitmentConfig::confirmed(),
    );
    let recent_blockhash = client.get_latest_blockhash().await?;

    // Generate a new keypair for the fee payer
    let fee_payer = Keypair::new();

    // Airdrop 1 SOL to fee payer
    let airdrop_signature = client
        .request_airdrop(&fee_payer.pubkey(), 1_000_000_000)
        .await?;

    loop {
        let confirmed = client.confirm_transaction(&airdrop_signature).await?;
        if confirmed {
            break;
        }
    }

    // Generate keypair to use as address of mint
    let mint = Keypair::new();

    let space = Mint::LEN;
    let rent = client.get_minimum_balance_for_rent_exemption(space).await?;

    // Create account instruction
    let create_account_instruction = create_account(
        &fee_payer.pubkey(),      // fee payer
        &mint.pubkey(),           // mint address
        rent,                     // rent
        space as u64,             // space
        &token_2022_program_id(), // program id
    );

    // Initialize mint instruction
    let initialize_mint_instruction = initialize_mint(
        &token_2022_program_id(),
        &mint.pubkey(),            // mint address
        &fee_payer.pubkey(),       // mint authority
        Some(&fee_payer.pubkey()), // freeze authority
        9,                         // decimals
    )?;

    // Create transaction and add instructions
    let transaction = Transaction::new_signed_with_payer(
        &[create_account_instruction, initialize_mint_instruction],
        Some(&fee_payer.pubkey()),
        &[&fee_payer, &mint],
        recent_blockhash,
    );

    // Send and confirm transaction
    let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;

    println!("Mint Address: {}", mint.pubkey());
    println!("Transaction Signature: {}", transaction_signature);

    let account_info = client.get_account(&mint.pubkey()).await?;
    println!("{:#?}", account_info);

    let mint_account = Mint::unpack(&account_info.data)?;
    println!("{:#?}", mint_account);

    Ok(())
}
```

</CodeTabs>

### System accounts

Accounts that remain owned by the System Program after creation are called
system accounts. Sending SOL to a new address for the first time creates a new
account at that address owned by the System Program.

All wallet accounts are system accounts. The fee payer on a transaction must be
a system account, because only System Program-owned accounts can pay
[transaction fees](/docs/core/fees).

![A wallet owned by the System Program containing 1,000,000 lamports](/assets/docs/core/accounts/system-account.svg)

The following example generates a new keypair, funds it with SOL, and fetches
the account. The `owner` field is `11111111111111111111111111111111` (the
[System Program](/docs/core/programs/builtin-programs#the-system-program)).

<CodeTabs storage="accounts" flags="r">

```ts !! title="Kit"
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer } from "@solana/kit-plugin-signer";

const client = await createClient()
  .use(generatedPayer())
  .use(
    solanaRpc({
      rpcUrl: "http://localhost:8899",
      rpcSubscriptionsUrl: "ws://localhost:8900"
    })
  )
  .use(rpcAirdrop());

// Generate a new keypair
const keypair = await generateKeyPairSigner();
console.log(`Public Key: ${keypair.address}`);

// Funding an address with SOL automatically creates an account
const signature = await client.airdrop(
  keypair.address,
  lamports(1_000_000_000n)
);

const accountInfo = await client.rpc.getAccountInfo(keypair.address).send();
console.log(accountInfo);
```

```ts !! title="Legacy"
import { Keypair, Connection, LAMPORTS_PER_SOL } from "@solana/web3.js";

// Generate a new keypair
const keypair = Keypair.generate();
console.log(`Public Key: ${keypair.publicKey}`);

// Create a connection to the Solana cluster
const connection = new Connection("http://localhost:8899", "confirmed");

// Funding an address with SOL automatically creates an account
const signature = await connection.requestAirdrop(
  keypair.publicKey,
  LAMPORTS_PER_SOL
);
await connection.confirmTransaction(signature, "confirmed");

const accountInfo = await connection.getAccountInfo(keypair.publicKey);
console.log(JSON.stringify(accountInfo, null, 2));
```

```rs !! title="Rust"
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signer::{keypair::Keypair, Signer},
};

#[tokio::main]
async fn main() -> Result<()> {
    // Generate a new keypair
    let keypair = Keypair::new();
    println!("Public Key: {}", keypair.pubkey());

    // Create a connection to Solana cluster
    let connection = RpcClient::new_with_commitment(
        "http://localhost:8899".to_string(),
        CommitmentConfig::confirmed(),
    );

    // Funding an address with SOL automatically creates an account
    let signature = connection
        .request_airdrop(&keypair.pubkey(), LAMPORTS_PER_SOL)
        .await?;

    loop {
        let confirmed = connection.confirm_transaction(&signature).await?;
        if confirmed {
            break;
        }
    }

    let account_info = connection.get_account(&keypair.pubkey()).await?;
    println!("{:#?}", account_info);

    Ok(())
}
```

</CodeTabs>

### Sysvar accounts

[Sysvar accounts](https://github.com/anza-xyz/agave/blob/v3.1.8/program-runtime/src/sysvar_cache.rs#L31)
are special accounts at predefined addresses that provide read-only access to
cluster state data. They update dynamically each slot.

| Sysvar                                                                                                                 | Address                                       | Purpose                                                 |
| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------- |
| [Clock](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2064)                                       | `SysvarC1ock11111111111111111111111111111111` | Current slot, epoch, and Unix timestamp                 |
| [EpochSchedule](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2264)                               | `SysvarEpochSchedu1e111111111111111111111111` | Epoch scheduling constants set in genesis               |
| [EpochRewards](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L24) | `SysvarEpochRewards1111111111111111111111111` | Epoch rewards distribution status and progress          |
| [Rent](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2255)                                        | `SysvarRent111111111111111111111111111111111` | Rental rate and exemption threshold                     |
| [SlotHashes](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2196)                                  | `SysvarS1otHashes111111111111111111111111111` | Most recent hashes of the slot's parent banks           |
| [StakeHistory](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2273)                                | `SysvarStakeHistory1111111111111111111111111` | Stake activations and deactivations per epoch           |
| [LastRestartSlot](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2123)                             | `SysvarLastRestartS1ot1111111111111111111111` | Last cluster restart slot                               |
| [Instructions](https://github.com/anza-xyz/agave/blob/v3.1.8/svm/src/account_loader.rs#L736)                           | `Sysvar1nstructions1111111111111111111111111` | Serialized instructions of the current transaction      |
| [SlotHistory](https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L2182)                                 | `SysvarS1otHistory11111111111111111111111111` | Record of which slots were produced over the last epoch |

The following example fetches and deserializes the Sysvar Clock account.

<CodeTabs storage="accounts" flags="r">

```ts !! title="Kit"
import { createClient } from "@solana/kit";
import { solanaRpc } from "@solana/kit-plugin-rpc";
import { generatedPayer } from "@solana/kit-plugin-signer";
import { fetchSysvarClock, SYSVAR_CLOCK_ADDRESS } from "@solana/sysvars";

const client = await createClient()
  .use(generatedPayer())
  .use(
    solanaRpc({
      rpcUrl: "https://api.mainnet.solana.com"
    })
  );

const accountInfo = await client.rpc
  .getAccountInfo(SYSVAR_CLOCK_ADDRESS, { encoding: "base64" })
  .send();
console.log(accountInfo);

// Automatically fetch and deserialize the account data
const clock = await fetchSysvarClock(client.rpc);
console.log(clock);
```

```ts !! title="Legacy"
import { Connection, SYSVAR_CLOCK_PUBKEY } from "@solana/web3.js";
import { getSysvarClockCodec } from "@solana/sysvars";

const connection = new Connection(
  "https://api.mainnet.solana.com",
  "confirmed"
);

const accountInfo = await connection.getAccountInfo(SYSVAR_CLOCK_PUBKEY);

// Deserialize the account data
const decodedClock = getSysvarClockCodec().decode(
  new Uint8Array(accountInfo?.data ?? [])
);

console.log(decodedClock);
// !collapse(1:16) collapsed
console.log(
  JSON.stringify(
    accountInfo,
    (key, value) => {
      if (key === "data" && value && value.length > 1) {
        return [
          value[0],
          "...truncated, total bytes: " + value.length + "...",
          value[value.length - 1]
        ];
      }
      return value;
    },
    2
  )
);
```

```rs !! title="Rust"
use anyhow::Result;
use bincode::deserialize;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::sysvar::{self, clock::Clock};

#[tokio::main]
async fn main() -> Result<()> {
    let connection = RpcClient::new_with_commitment(
        "https://api.mainnet.solana.com".to_string(),
        CommitmentConfig::confirmed(),
    );

    let account = connection.get_account(&sysvar::clock::ID).await?;
    // Deserialize the account data
    let clock: Clock = deserialize(&account.data)?;

    println!("{:#?}", account);
    println!("{:#?}", clock);

    Ok(())
}
```

</CodeTabs>
