att
att

Reputation: 645

Solana Anchor passing Non Owned Account Info to Transfer SOL

I need a program that distributes SOLs owned by PDA to another account.

Defined the context for distribute instruction as below.

#[derive(Accounts)]
pub struct Pool<'info> {
    #[account(
        mut, 
        seeds = [
            b"pool_account".as_ref()], 
            bump = pool_account.pool_account_bump
        )
    ]
    pool_account: Account<'info, PoolState>,
    #[account()]
    soho_account: Account<'info, PoolState>
}

The distribute instruction is

pub fn distribute(ctx: Context<Pool>) -> ProgramResult {
        let pool_account = &ctx.accounts.pool_account;
        let pool_account_balance = pool_account.to_account_info().lamports();
        let soho_account = &ctx.accounts.soho_account;

        println!("Distributing {:?} lamports ", pool_account_balance,);

        let from_pub_key = pool_account.to_account_info().key;
        let to_pub_key = soho_account.to_account_info().key;
        let ix = system_instruction::transfer(from_pub_key, to_pub_key, pool_account_balance);
        anchor_lang::solana_program::program::invoke(
            &ix,
            &[
                ctx.accounts.pool_account.to_account_info(),
                soho_account.to_account_info()
            ],
        )?;

        Ok(())
    }

The test

it(`Distrubutes funds to shareholders`, async () => {
    let [poolAccount, poolAccountBump] = await web3.PublicKey.findProgramAddress(
      [Buffer.from("pool_account")],
      program.programId
    )

    // Airdrop SOL to pool account PDA
    const connection = await anchor.getProvider().connection;
    let signature = await connection.requestAirdrop(poolAccount, LAMPORTS_PER_SOL)
    await connection.confirmTransaction(signature);

    balance = await connection.getBalance(poolAccount);
    console.log(`Pool Account: ${poolAccount} balance: ${balance}`)
    assert.ok(balance == 1000953520, `Program account has ${balance}`)

    const sohoAccount = anchor.web3.Keypair.generate();
    signature = await connection.requestAirdrop(sohoAccount.publicKey, LAMPORTS_PER_SOL)
    await connection.confirmTransaction(signature);

    await program.rpc.distribute({
      accounts: {
        poolAccount: poolAccount,
        sohoAccount: sohoAccount.publicKey,
        // soho2: soho2.publicKey,
      }
    })

  })

When I run the test for the distribute instruction, the test fails w/ Error: 3007: The given account is owned by a different program than expected message.

Basically, it expects the soho_account to be owned by the program. however it should be an external account.

Upvotes: 1

Views: 2487

Answers (1)

Frank C.
Frank C.

Reputation: 8098

The system transfer requires that, at a minimum, the from account is a System Owned account. Not a PDA and not a Program Owned account.

If the PDA is owned by your program then your program can transfer from it to ANY account without invoking the system transfer.

See the cookbook: https://solanacookbook.com/references/programs.html#transferring-lamports

Upvotes: 2

Related Questions