如何将 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_signed
和 system_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,而是你的程序试图这样做,所以运行时会再次说“这是不允许的”。
如果PDA一旦创建为关联代币地址,就不能将SOL从PDA转移到另一个账户吗?
我想使用一个 PDA 帐户同时转移 SOL 和 SPL-Token。
我尝试了 solana_program::program::invoke_signed
和 system_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,而是你的程序试图这样做,所以运行时会再次说“这是不允许的”。