Skip to main content
Sigvex

Readonly Account Mutation Remediation

How to fix readonly account mutation logic errors.

Readonly Account Mutation Remediation

Overview

Detector Reference: Readonly Account Mutation

This guide explains how to fix logic errors where programs verify an account is readonly then attempt to modify it.

Determine the correct intent and align the check with the operation:

// If the account SHOULD be modified:
if !account.is_writable {
    return Err(ProgramError::InvalidAccountData);
}
let mut data = account.data.borrow_mut();
data[8..16].copy_from_slice(&new_value.to_le_bytes());

// If the account should be READONLY:
// Simply remove the modification code
let data = account.data.borrow();
let value = u64::from_le_bytes(data[8..16].try_into()?);

Alternative Mitigations

  1. Separate instruction handlers: create distinct instruction handlers for read-only and read-write operations so the mutability constraint is structurally enforced.
  2. Anchor typed constraints: use #[account(mut)] for writable accounts and undecorated Account<'info, T> for readonly, making the intent explicit at the type level.

Common Mistakes

  • Inverted condition: writing if account.is_writable { return Err(...) } when you meant if !account.is_writable { return Err(...) }. Double-check the boolean logic.
  • Checking the wrong account: verifying account A is readonly but modifying account B, which shares a similar variable name.
  • Dead code: the readonly check may be leftover from a refactor. Remove unused checks to avoid confusion.

References