I am working on a simple project, where a wallet account is created, credited some SOL, then assigned a program contract as its owner (via assign instruction). In the use case, the program should control the wallet's account funds.

The program code updates the balance of the owned account.

However, it is failing with a "instruction changed the balance of a read-only account" (which intrigues me, considering Solana Docs - https://docs.solana.com/developing/programming-model/accounts#:~:text=The%20security%20model%20enforces%20that,them%20via%20accounts%20they%20own.)

Transaction Instruction keys hold references to both accounts as 'isWritable: true', and transaction is signed with the program's key.

Any guidance is much appreciated.

The code is as follows:

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let owned_account = next_account_info(accounts_iter)?;

    let owner = next_account_info(accounts_iter)?;

    msg!("Owned Account: {:?}", owned_account);
    msg!("Owner: {:?}", owner);

    if owned_account.owner != program_id {
        msg!("This account is not owned by the program");
        return Err(ProgramError::IncorrectProgramId);

    if owned_account.lamports() < 111111111 {
        msg!("Insufficient funds in the owned account");
        return Err(ProgramError::InsufficientFunds);

    **owned_account.lamports.borrow_mut() -= 111111111;
    **owner.lamports.borrow_mut() += 111111111;

    msg!("Transferred 111111111 lamports from the owned account to the owner");

    // gracefully exit the program

1 Answer 1


Not sure about the cause of the error you're getting, but here are two examples from the Solana Developers repo you can reference for using PDAs and transferring SOL within a native Solana program.



  • I think I understand what I'm doing wrong. I have two accounts: owned data account A (holding origin funds), program account P, that owns the first. My instruction attempts to transfer from account A to the destination (account P). Apparently there are 2 facts about account model I missed: a) An account can only transfer SOL if it’s owned by SystemProgram. But account A is owned by program P; b) An executable account is read-only, therefore I cannot update P’s lamports directly (hence the error). I need a 3rd vault account, V, whereas account A can only be used for P’s data storage?
    – Tiago A
    Apr 2, 2023 at 14:42
  • It works now. a) An account can only transfer SOL if it’s owned by SystemProgram This is only true if done via the Transfer instruction. Updating the lamports state directly (as done in code above) works. Just had to do it via a third account, V. Thanks
    – Tiago A
    Apr 2, 2023 at 18:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.