Instruction tries to borrow reference for an account which is already borrowed

911 views Asked by At

I am writing a Solana smart contract that integrates with Mango Markets, and am having an error that I am struggling to nail down. Here is the offending code:

let mango_acc_info_clone = &ctx.accounts.mango_account.clone();
let res = MangoAccount::load_mut_checked(
    mango_acc_info_clone,
    &ctx.accounts.mango_program_id.key().clone(),
    &ctx.accounts.mango_group.key().clone(),
);
let mango_account = match res {
    Ok(acc) => acc,
    Err(error) => panic!("failed to place perp order: {:?}", error),
};

let accounts = [
    ctx.accounts.mango_program_id.clone(),
    ctx.accounts.mango_group.clone(),
    ctx.accounts.mango_account.clone(),
    ctx.accounts.mango_account_owner.clone(),
    ctx.accounts.mango_cache.clone(),
    ctx.accounts.mango_perp_market.clone(),
    ctx.accounts.mango_bids.clone(),
    ctx.accounts.mango_asks.clone(),
    ctx.accounts.mango_event_queue.clone(),
];

let result = mango::instruction::place_perp_order(
    &ctx.accounts.mango_program_id.key().clone(),
    &ctx.accounts.mango_group.key().clone(),
    &ctx.accounts.mango_account.key().clone(),
    &ctx.accounts.mango_account_owner.key().clone(),
    &ctx.accounts.mango_cache.key().clone(),
    &ctx.accounts.mango_perp_market.key().clone(),
    &ctx.accounts.mango_bids.key().clone(),
    &ctx.accounts.mango_asks.key().clone(),
    &ctx.accounts.mango_event_queue.key().clone(),
    None,
    &mango_account.spot_open_orders,
    converted_side,
    price,
    quantity,
    client_order_id,
    converted_order_type,
    reduce_only,
);

let is = match result {
    Ok(is) => is,
    Err(error) => panic!("failed to place order: {:?}", error),
};

invoke_signed(
    &is,
    &accounts,
    &[&[
        MANGO_OWNER_PDA_SEED,
        ctx.accounts.vault.name.as_ref(),
        &[_mango_owner_bump],
    ]],
)

I believe the error to be with the load_mut_checked function, which is returning a RefMut<MangoAccount> type. Here is the error:

solana.rpc.core.RPCException: {'code': -32002, 'message': 'Transaction simulation failed: Error processing Instruction 0: instruction tries to borrow reference for an account which is already borrowed', 'data': {'accounts': None, 'err': {'InstructionError': [0, 'AccountBorrowFailed']}, 'logs': ['Program AQPDVpAsDtd8cfXVjrUEKrhchF4cYwST2wyq3tJa82ci invoke [1]', 'Program log: Debugging', 'Program log: 3', 'Program log: Failed to borrow a reference to account data, already borrowed', 'Program AQPDVpAsDtd8cfXVjrUEKrhchF4cYwST2wyq3tJa82ci consumed 13081 of 200000 compute units', 'Program AQPDVpAsDtd8cfXVjrUEKrhchF4cYwST2wyq3tJa82ci failed: instruction tries to borrow reference for an account which is already borrowed'], 'unitsConsumed': 0}}

I have tried calling .to_owned() on the account, but that just gives me a different error:

solana.rpc.core.RPCException: {'code': -32002, 'message': 'Transaction simulation failed: Error processing Instruction 0: Program failed to complete', 'data': {'accounts': None, 'err': {'InstructionError': [0, 'ProgramFailedToComplete']}, 'logs': ['Program AQPDVpAsDtd8cfXVjrUEKrhchF4cYwST2wyq3tJa82ci invoke [1]', 'Program AQPDVpAsDtd8cfXVjrUEKrhchF4cYwST2wyq3tJa82ci consumed 5013 of 200000 compute units', 'Program failed to complete: Access violation in stack frame 7 at address 0x200007ae0 of size 8 by instruction #52535', 'Program AQPDVpAsDtd8cfXVjrUEKrhchF4cYwST2wyq3tJa82ci failed: Program failed to complete'], 'unitsConsumed': 0}}

Any help would be great. I am very new to Rust and Solana programming so let me know if any additional information can help. I am running this on a local validator.

1

There are 1 answers

1
Jon C On

I believe the error comes from trying to mutably borrow and immutably borrow at the same time, which is not allowed for RefCell: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.try_borrow

Since the cross-program invocation at invoke_signed may need to mutably borrow data, the easiest way to fix this is to:

  • avoid mutably borrowing data, perhaps by using something like MangoAccount::load_checked if it exists
  • drop all references before calling invoke_signed, using drop(mango_account) once you're done with it