Modification Rules

Summary

Runtime checks after each instruction: only the owner can debit lamports or modify data, data can grow max 10 KiB per instruction, owner changes require zero-initialized data, and the executable flag is irreversible.

The Solana runtime enforces these rules after each instruction executes via BorrowedInstructionAccount methods. Each rule is checked at the point of modification, and the transaction is rolled back if any check fails.

Lamports rules

RuleEnforcementError
Only the owner can debit lamportsset_lamports(): checks is_owned_by_current_program() when lamports < currentExternalAccountLamportSpend
Read-only accounts cannot have lamports changedset_lamports(): checks is_writable()ReadonlyLamportChange
Any program can credit lamports to a writable accountset_lamports(): the ownership check only applies when the new balance is less than the current balance; the writable check still appliesReadonlyLamportChange
Lamports must balance across an instructionTransactionContext::pop(): verifies get_lamports_delta() == 0UnbalancedInstruction

Data rules

RuleEnforcementError
Only the owner can modify datacan_data_be_changed(): checks is_owned_by_current_program()ExternalAccountDataModified
Read-only accounts cannot have data modifiedcan_data_be_changed(): checks is_writable()ReadonlyDataModified
Only the owner can resize datacan_data_be_resized(): checks is_owned_by_current_program() when new_len != old_lenAccountDataSizeChanged
Max data size: 10 MiBTransactionAccounts::can_data_be_resized(): checks new_len <= MAX_ACCOUNT_DATA_LENInvalidRealloc
Max growth per instruction: 10 KiBDeserialization in deserialize_parameters_aligned(): checks post_len - pre_len <= MAX_PERMITTED_DATA_INCREASEInvalidRealloc
Max growth per transaction: 20 MiBTransactionAccounts::can_data_be_resized(): checks cumulative resize_delta <= MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTIONMaxAccountsDataAllocationsExceeded

Owner rules

RuleEnforcementError
Only the current owner can reassign the ownerset_owner(): checks is_owned_by_current_program()ModifiedProgramId
Account must be writableset_owner(): checks is_writable()ModifiedProgramId
Data must be zero-initializedset_owner(): checks is_zeroed(data)ModifiedProgramId

Executable flag rules

RuleEnforcementError
Account must be rent-exemptset_executable(): checks rent.is_exempt(lamports, data_len)ExecutableAccountNotRentExempt
Only the owner can set the flagset_executable(): checks is_owned_by_current_program()ExecutableModified
Account must be writableset_executable(): checks is_writable()ExecutableModified

Rent state transitions

Accounts exist in one of three RentState values: Uninitialized (0 lamports), RentPaying (above 0 but below the rent-exempt minimum), and RentExempt (at or above the minimum). Disallowed transitions produce TransactionError::InsufficientFundsForRent.

The runtime enforces these rules via transition_allowed():

  • Any account can transition to Uninitialized (close) or RentExempt.
  • No account can enter RentPaying from Uninitialized or RentExempt. All new accounts must be rent-exempt.

Account borrow rules

During instruction execution, the runtime enforces single-writer borrow semantics on accounts. A program can obtain either one mutable reference or multiple immutable references to an account, but not both simultaneously. If a program attempts to borrow an account that is already mutably borrowed (or mutably borrow an account that is already immutably borrowed), the instruction fails with AccountBorrowFailed via try_borrow() and try_borrow_mut(). If an instruction completes while a borrow is still outstanding, the runtime returns AccountBorrowOutstanding in TransactionContext::pop().

Is this page helpful?

Table of Contents

Edit Page

Managed by

© 2026 Solana Foundation.
All rights reserved.
Get connected