---
title: Accounts and Transaction Context
description: Read and mutate accounts safely in Rust with AccountManager, plus
  transaction, block, and shadow stack context.
source_url:
  html: https://thru.org/docs/sdks/rust-program-sdk/accounts-and-transaction-context/
  md: https://thru.org/docs/sdks/rust-program-sdk/accounts-and-transaction-context.md
---

# Accounts and Transaction Context

Use this page when you need to read or write account data, or read transaction and block context, from a Rust program.

## `AccountManager`

`AccountManager<NUM_ACCOUNTS>` is the safe entry point for account access. It tracks borrows at runtime so two live references to the same account cannot alias, and it enforces the transaction’s own mutability rules.

`NUM_ACCOUNTS` is the maximum number of *distinct* accounts you can have borrowed at the same time, not the number of accounts in the transaction. A power of two performs best; `next_pow2` helps:

```rust
use thru_core::{next_pow2, AccountManager};

const NUM_ACCOUNTS: usize = next_pow2(10); /* 16 */
```

Declare it in your entry signature and the `#[entry]` macro constructs it for you:

```rust
#[entry(stack_size = 8192)]
fn main(instr_data: &[u8], mgr: AccountManager<4>) -> Result<u64, u64> { /* ... */ }
```

To build it yourself:

```rust
use thru_core::{get_txn, AccountManager};

let mgr = AccountManager::<4>::from_txn(get_txn())?;
```

`from_txn` fails with `AccountError::UnsupportedTxnVersion` for any transaction version other than 1.

## Account roles

| Index | Role | Mutable |
| - | - | - |
| `0` | `AccountType::FeePayer` | yes |
| `1` | `AccountType::Program` | no |
| `2 .. 2 + readwrite_cnt` | `AccountType::ReadWrite` | yes |
| remaining | `AccountType::ReadOnly` | no |

`mgr.accounts_count()` returns the total, `mgr.account_role(idx)` the role, and `mgr.is_mutable(idx)` whether the role allows mutation.

## Borrowing accounts

```rust
let mut account = mgr.get(2)?;                  /* role-appropriate borrow */
let bytes = account.data();                     /* &[u8] */
if let Some(bytes) = account.data_mut() {       /* Some only for mutable roles */
    bytes[0] = 1;
}

let readonly = mgr.get_readonly(2)?;            /* force an immutable borrow */
```

| Method | Returns |
| - | - |
| `get(idx)` | Mutable borrow for fee-payer and read-write accounts, immutable otherwise. |
| `get_readonly(idx)` | Always an immutable borrow, whatever the role. |
| `accounts_iter()` | `(index, AccountRef)` pairs in wire order, skipping accounts that cannot currently be borrowed. |
| `is_borrowed(idx)` | Whether that account has a live borrow. |
| `has_active_borrows()` | Whether any account has a live borrow. |

`get` and `get_readonly` return `AccountError` when the index is out of bounds, the account is already borrowed incompatibly, more than `NUM_ACCOUNTS` distinct accounts have been touched, or the account info cannot be read.

## Reading an `AccountRef`

| Accessor | Value |
| - | - |
| `data()` | Account data as `&[u8]`. |
| `data_mut()` | `Some(&mut [u8])` for mutable borrows, `None` otherwise. |
| `owner()` / `owner_bytes()` | Owner as `&Pubkey` or `&[u8; 32]`. |
| `is_owned_by_current_program()` | Whether the owner is the executing program. |
| `balance()` | Native balance. |
| `data_size()` | Data length recorded in metadata. |
| `nonce()` | Account nonce. |
| `is_mutable()` | Whether this borrow can write. |
| `account_type()` | The `AccountType` of the borrow. |

Data is reachable only through these accessors: the borrow guard lives in the `AccountRef`, so references cannot outlive it.

## Writing account data

Account data segments start read-only. Mark the segment writable before writing, and drop every borrow of the account first, because the mutating operations panic while a borrow is live:

```rust
use thru_core::syscall::SyscallCode;

if mgr.set_account_data_writable(2) != SyscallCode::Success {
    return Err(1);
}

let mut account = mgr.get(2)?;
if let Some(data) = account.data_mut() {
    data[0] = 42;
}
```

Mutating helpers on `AccountManager` (`set_account_data_writable`, `account_transfer`, `account_resize`, `account_create`, `create_and_init`, `account_delete`, `account_compress`, `account_decompress`, `account_set_flags`, `account_create_eoa`, `invoke`) are listed in [Syscalls](https://thru.org/docs/sdks/rust-program-sdk/syscalls.md).

## Transaction context

`mgr.txn` is the parsed transaction; `thru_core::get_txn()` returns it without a manager.

| Method | Value |
| - | - |
| `account_pubkeys()` / `account_pubkey(idx)` | Account addresses referenced by the transaction. |
| `program_pubkey()` | Address of the invoked program. |
| `instr_data()` | Instruction data (the same bytes passed to your entry function). |
| `accounts_cnt()` / `readwrite_accounts_cnt()` / `readonly_accounts_cnt()` | Account counts. |
| `is_account_idx_writable(idx)` | Whether the transaction marked that index writable. |
| `fee()`, `nonce()`, `chain_id()` | Fee, nonce, and chain id. |
| `start_slot()`, `expiry_slot()` | Validity window. |
| `requested_compute_units()`, `requested_mem_units()` | Requested resource limits. |
| `fee_payer_proof()`, `fee_payer_meta()` | Fee-payer state proof and metadata, when present. |

## Block context

```rust
use thru_core::{mem::get_block_ctx, types::block_ctx::BlockCtx};

let block: &BlockCtx = get_block_ctx();
let now_ns = block.block_time;
```

`BlockCtx` exposes `slot`, `block_time` (Unix nanoseconds), `block_price`, `state_root`, `cur_block_hash`, `block_producer`, and `weight_slot`. `get_past_block_ctx(blocks_ago)` returns an older context, or `None` when it is older than the chain’s current slot.

## Shadow stack

```rust
use thru_core::get_shadow_stack;

let stack = get_shadow_stack();
let depth = stack.call_depth();
let program_idx = stack.current_program_acc_idx();
```

`ShadowStack` also exposes `max_call_depth()`, `current_total_stack_pages()`, `current_total_heap_pages()`, `get_frame(idx)`, `get_parent_frame()`, and `get_current_frame()`. Use it to reason about invocation depth and callers; see [Cross-Program Invocation](https://thru.org/docs/sdks/rust-program-sdk/cross-program-invocation.md).

## Authorization helpers

```rust
use thru_core::program_utils;

program_utils::is_account_authorized_by_idx(idx);
program_utils::is_account_authorized_by_pubkey(&pubkey);
program_utils::is_account_idx_owned_by_current_program(idx);
program_utils::is_program_reentrant();
```

An account counts as authorized when it is the fee payer, the current program, part of the invocation chain, or explicitly delegated by a caller frame that owns it.

## Packed structs

Account layouts are usually `#[repr(C, packed)]`. Read and write potentially misaligned fields through the macros instead of taking references:

```rust
use thru_core::{read_packed_field, write_packed_field};

let amount = read_packed_field!(token_account, amount);
write_packed_field!(token_account, amount, amount + 1);
```

## Related pages

- [Syscalls](https://thru.org/docs/sdks/rust-program-sdk/syscalls.md)
- [Error Handling and Return Codes](https://thru.org/docs/sdks/rust-program-sdk/error-handling-and-return-codes.md)
- [Accounts specification](https://thru.org/docs/spec/accounts/overview.md)
