4

If once a PDA is created as an associate token address, it can not transfer SOL from the PDA to another account?

I'd like to transfer both SOL and SPL-Token using a single PDA account.

I tried both solana_program::program::invoke_signed with system_instruction::transfer and also try_borrow_mut_lamports(). But, it is not working.

Whenever transfer with system_program, I got Transfer: from must not carry data, the other way was instruction spent from the balance of an account it does not own.

What am I missing?

Can I get some advice?

Thx.

the associate token account in Struct

#[account(init, 
    seeds = [authority.to_account_info().key.as_ref()], 
    bump, 
    payer = authority, 
    token::mint = mint, 
    token::authority = authority)
]    
pub vault: Account<'info, TokenAccount>,

Create another PDA, authority for when trasfering spl-token from the vault account

let (vault_authority, vault_authority_bump) = Pubkey::find_program_address(
    &[ctx.accounts.vault.to_account_info().key.as_ref()], ctx.program_id
);

let cpi_accounts = SetAuthority {
    account_or_mint: ctx.accounts.vault.to_account_info().clone(),
    current_authority: ctx.accounts.authority.to_account_info().clone()
};
let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info().clone(), cpi_accounts);
token::set_authority(cpi_context, AuthorityType::AccountOwner, Some(vault_authority))?;

Failed case 1, transfer SOL from vault to owner using system_program

// Error message : Transfer: `from` must not carry data
let vault_key = ctx.accounts.authority.to_account_info().key.as_ref();
let (_vault, bump) = Pubkey::find_program_address(&[vault_key], ctx.program_id);
let seeds = &[vault_key, &[bump]];
let signer = &[&seeds[..]];

let owner_key = &ctx.accounts.owner.key();
let vault_key = &ctx.accounts.vault.key();
let ix = system_instruction::transfer(vault_key, owner_key, amount);

let owner_account = ctx.accounts.owner.to_account_info();
let vault_account = ctx.accounts.vault.to_account_info();
solana_program::program::invoke_signed(&ix, &[vault_account, owner_account], signer)?;

Failed case 2, transfer SOL from vault to owner using 'try_borrow_mut_lamports'

// Error message : instruction spent from the balance of an account it does not own
**ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= amount;
**ctx.accounts.owner.to_account_info().try_borrow_mut_lamports()? += amount;
  • hey Kenneth :) do u have a github repo of this project by any chance? im trying to figure out how to send and withdraw sol to/from pda. – Noyan Alimov Jun 19 '22 at 12:40
  • PDA can be a normal wallet account, so I think this link may help you. https://github.com/coral-xyz/anchor/blob/698426033052781988cd7980249726501ae08bdc/tests/misc/programs/misc/src/lib.rs#L281 And this link could help you as well. https://github.com/coral-xyz/anchor/blob/55e0c5d9dd356d4dd5404f672489fb24fb3cdcdd/tests/escrow/programs/escrow/src/lib.rs#L73 Generally, Anchor frameworks GitHub repository has examples of various typed projects. It is a kind of textbook to me. https://github.com/coral-xyz/anchor – Kenneth Heo Jun 21 '22 at 00:10

3 Answers3

3

It is not possible to transfer both SOL and SPL tokens from the same account. The SPL token account (associated or not) is owned by the SPL token program, which means that only the SPL token program can change its data and deduct its lamports. Otherwise, it would be possible for anyone to bring a token account under rent-exemption and break guarantees of the token program.

Let's go through your cases and explain why they didn't work:

  • Failed case 1, transfer SOL from vault to owner using system_program triggering: Transfer: from must not carry data

There's actually two errors here, but only the first one is reported. When you try to system_instruction::transfer from the vault, it will mainly fail because the vault is not owned by the system program. It's owned by the token program, so only the token program can move its lamports, and not the system program. The reported failure says that the account also has data, which is the token data (owner, amount, delegate, etc).

  • Failed case 2, transfer SOL from vault to owner using 'try_borrow_mut_lamports'

This one's very similar -- instead of the system program trying to deduct lamports from the token account, it's your program trying to do it, so the runtime will again say "this isn't allowed".

Jon C
  • 7,019
  • 10
  • 17
1

Since your PDA has data, and you don't intend on closing the account, you must keep a minimum balance for rent exemption, otherwise new errors associated to this will arise. For this, you can pass in an amount derived in the client-side app specific to the data size of your account. Otherwise, you can simply do the following:

pub fn handler(ctx: Context<PayoutFromVault>, minimum_balance_for_rent_exemption: u64) -> Result<()> {
    let vault_account_info: &mut AccountInfo = &mut ctx.accounts.vault.to_account_info();
    let owner_account_info: &mut AccountInfo = &mut ctx.accounts.owner.to_account_info();

    let vault_lamports_initial = vault_account_info.lamports();
    let owner_lamports_initial = owner_account_info.lamports();

    let transfer_amount = vault_lamports_initial.try_sub(minimum_balance_for_rent_exemption)?;

    **owner_account_info.lamports.borrow_mut() = owner_lamports_initial.try_add(transfer_amount)?;
    **vault_account_info.lamports.borrow_mut() = minimum_balance_for_rent_exemption;

    msg!("{} lamports transferred from vault to {}", transfer_amount, ctx.accounts.owner.key());
    Ok(())
}
Andrew
  • 904
  • 5
  • 17
SolCharms
  • 11
  • 3
-1

In Solana, all accounts are owned by a program. Your normal account is owned by the system program, whereas your token account is owned by the token program. You can't use the system program to sign off for the token account.Hence the error "transfer can't contain data".

  • Whenever thought about this concept, it is not reaching directly to me however thinking about which account belongs is a very important point. I should better plan to take a time to make sure catch the concept in the near time. Thanks! – Kenneth Heo Jun 21 '22 at 00:24