如何将 SOL 从创建令牌关联帐户的 PDA 转移到 Anchor 中的普通帐户?

How to transfer SOL from a PDA which created a token associate account to a normal account in Anchor?

如果PDA一旦创建为关联代币地址,就不能将SOL从PDA转移到另一个账户吗?

我想使用一个 PDA 帐户同时转移 SOL 和 SPL-Token。

我尝试了 solana_program::program::invoke_signedsystem_instruction::transfer 以及 try_borrow_mut_lamports()。 但是,它不起作用。

每当用system_program转账时,我从 must not carry data得到Transfer: ,另一种方式是instruction spent from the balance of an account it does not own

我错过了什么?

我能得到一些建议吗?

谢谢。

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>,

创建另一个PDA,从保险库账户转移spl-token时的权限

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))?;

案例 1 失败,使用 system_program

将 SOL 从保险库转移到所有者
// 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)?;

案例 2 失败,使用 'try_borrow_mut_lamports'

将 SOL 从保险库转移到所有者
// 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;

在 Solana 中,所有帐户都属于一个程序。您的普通帐户归系统程序所有,而您的令牌帐户则归令牌程序所有。您不能使用系统程序为令牌签注account.Hence 错误“传输不能包含数据”。

无法从同一帐户转移 SOL 和 SPL 代币。 SPL 代币账户(关联或非关联)归 SPL 代币程序所有,这意味着只有 SPL 代币程序可以更改其数据并扣除其 lamports。否则,任何人都有可能在 rent-exemption 下使用令牌帐户并破坏令牌程序的保证。

让我们检查一下您的案例并解释它们不起作用的原因:

  • 案例 1 失败,使用 system_program 触发将 SOL 从金库转移到所有者:Transfer: from must not carry data

这里实际上有两个错误,但只报告了第一个。当您尝试从 vault system_instruction::transfer 时,它主要会失败,因为 vault 不属于系统程序。它归令牌程序所有,因此只有令牌程序可以移动它的 lamports,而系统程序不能。报告的失败表明该帐户也有数据,即令牌数据(所有者、数量、委托等)。

  • 案例 2 失败,使用 'try_borrow_mut_lamports'
  • 将 SOL 从保险库转移到所有者

这个非常相似——不是系统程序试图从令牌帐户中扣除 lamports,而是你的程序试图这样做,所以运行时会再次说“这是不允许的”。