Solana Anchor 如何从程序账户提取代币到用户账户?
Solana Anchor How to withdraw tokens from program account to user account?
我可以成功地将 USDC 代币从用户账户抵押到程序账户,
但是要提取 USDC 代币,它会在 "check4" 之后失败:
pub fn withdraw_usdc(ctx: Context<UsdcDoge>, amount: u64, nonce: u8) -> ProgramResult {
let seeds = &[ctx.accounts.usdc_mint.to_account_info().key.as_ref(), &[nonce], ];
let signer = &[&seeds[..]];
msg!("check3");
let cpi_accounts = Transfer {
from: ctx.accounts.usdc_pgid.to_account_info(),
to: ctx.accounts.usdc_user.to_account_info(),
authority: ctx.accounts.program_signer.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
msg!("check4");
token::transfer(cpi_ctx, amount).expect("transfer2 failed");//?;
Ok(())
}
...
#[derive(Accounts)]
pub struct UsdcDoge<'info> {
pub program_signer: AccountInfo<'info>,
#[account(signer)]//authority should sign this txn
pub authority: AccountInfo<'info>,
pub usdc_mint: CpiAccount<'info, Mint>,
#[account(mut, "usdc_user.owner == *authority.key")]
pub usdc_user: CpiAccount<'info, TokenAccount>,
#[account(mut)]
pub usdc_pgid: CpiAccount<'info, TokenAccount>,
#[account(mut,
"doge_mint.mint_authority == COption::Some(*program_signer.key)")]
pub doge_mint: CpiAccount<'info, Mint>,
#[account(mut, "doge_user.owner == *authority.key")]
pub doge_user: CpiAccount<'info, TokenAccount>,
#[account(mut)]
pub doge_pgid: CpiAccount<'info, TokenAccount>,
//pub program_id: Pubkey<'info>,
// We already know its address and that it's executable
#[account(executable,"token_program.key == &token::ID")]
pub token_program: AccountInfo<'info>,
pub system_program: AccountInfo<'info>,
}
'Program log: Error: owner does not match'
错误:发送交易失败:交易模拟失败:错误处理指令0:自定义程序错误:0x4
'Program log: --------------== withdraw_usdc',
'Program log: check1',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]',
'Program log: Instruction: Transfer',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3402 of 188791 compute units',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success',
'Program log: transfer1 successful. amount: 5000000',
'Program log: check3',
'Program log: check4',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]',
'Program log: Instruction: Transfer',
'Program log: Error: owner does not match',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2902 of 181411 compute units',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA failed: custom program error: 0x4',
'Program J1JQ8f2s77Xhx7xQCRZmMTK2j3HuahgeSMUuccL5ywnb consumed 200000 of 200000 compute units',
'Program J1JQ8f2s77Xhx7xQCRZmMTK2j3HuahgeSMUuccL5ywnb failed: custom program error: 0x4'
在锚脚本中:
await program.rpc.withdrawUsdc(
amt2, nonce, {
accounts: {
programSigner,
authority: userKeys.publicKey,
usdcMint,
usdcUser,
usdcPgid,
dogeMint,
dogeUser,
dogePgid,
tokenProgram: TOKEN_PROGRAM_ID,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [userKeys],
});
上面的accounts对象,amt2,nonce值和stakeUsdc函数调用中的完全一样...所以不知道楼主哪里错了...
在失败之前,USDC 在 usdc_pgid 帐户上,该帐户来自:
usdcPgid = await createTokenAccount(provider, usdcMint, program.programId);//arguments: privider, mint, owner
usdc_pgid 的所有者是 program.programId,所以我应该让 cpi_accounts 使用 programId 作为权限...比如:
authority: ctx.program_id.to_account_info(),
错误[E0599]: 在当前范围内找不到名为 to_account_info
的方法供参考 &anchor_lang::prelude::Pubkey
--> programs/doge/src/lib.rs:265:33
|
265 |权限:ctx.program_id.to_account_info(),
| ^^^^^^^^^^^^^^^ 在 &anchor_lang::prelude::Pubkey
中找不到方法
anchor_lang::CpiAccount
https://docs.rs/anchor-lang/0.13.2/anchor_lang/struct.CpiAccount.html
anchor_lang::序曲::公钥
https://docs.rs/anchor-lang/0.13.2/anchor_lang/prelude/struct.Pubkey.html
从上面的文档来看,Pubkey类型没有将Pubkey类型转换为AccountInfo类型的方法!
我该怎么做?
程序的令牌余额账户必须有一个签名者 PDA 作为所有者!
所以token余额账户的所有者必须是programSigner!
锚代码:
programVault = await createTokenAccount(provider, usdcMint, programSigner);
第三个参数必须是programSigner!!!
我可以成功地将 USDC 代币从用户账户抵押到程序账户, 但是要提取 USDC 代币,它会在 "check4" 之后失败:
pub fn withdraw_usdc(ctx: Context<UsdcDoge>, amount: u64, nonce: u8) -> ProgramResult {
let seeds = &[ctx.accounts.usdc_mint.to_account_info().key.as_ref(), &[nonce], ];
let signer = &[&seeds[..]];
msg!("check3");
let cpi_accounts = Transfer {
from: ctx.accounts.usdc_pgid.to_account_info(),
to: ctx.accounts.usdc_user.to_account_info(),
authority: ctx.accounts.program_signer.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
msg!("check4");
token::transfer(cpi_ctx, amount).expect("transfer2 failed");//?;
Ok(())
}
...
#[derive(Accounts)]
pub struct UsdcDoge<'info> {
pub program_signer: AccountInfo<'info>,
#[account(signer)]//authority should sign this txn
pub authority: AccountInfo<'info>,
pub usdc_mint: CpiAccount<'info, Mint>,
#[account(mut, "usdc_user.owner == *authority.key")]
pub usdc_user: CpiAccount<'info, TokenAccount>,
#[account(mut)]
pub usdc_pgid: CpiAccount<'info, TokenAccount>,
#[account(mut,
"doge_mint.mint_authority == COption::Some(*program_signer.key)")]
pub doge_mint: CpiAccount<'info, Mint>,
#[account(mut, "doge_user.owner == *authority.key")]
pub doge_user: CpiAccount<'info, TokenAccount>,
#[account(mut)]
pub doge_pgid: CpiAccount<'info, TokenAccount>,
//pub program_id: Pubkey<'info>,
// We already know its address and that it's executable
#[account(executable,"token_program.key == &token::ID")]
pub token_program: AccountInfo<'info>,
pub system_program: AccountInfo<'info>,
}
'Program log: Error: owner does not match'
错误:发送交易失败:交易模拟失败:错误处理指令0:自定义程序错误:0x4
'Program log: --------------== withdraw_usdc',
'Program log: check1',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]',
'Program log: Instruction: Transfer',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3402 of 188791 compute units',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success',
'Program log: transfer1 successful. amount: 5000000',
'Program log: check3',
'Program log: check4',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]',
'Program log: Instruction: Transfer',
'Program log: Error: owner does not match',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2902 of 181411 compute units',
'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA failed: custom program error: 0x4',
'Program J1JQ8f2s77Xhx7xQCRZmMTK2j3HuahgeSMUuccL5ywnb consumed 200000 of 200000 compute units',
'Program J1JQ8f2s77Xhx7xQCRZmMTK2j3HuahgeSMUuccL5ywnb failed: custom program error: 0x4'
在锚脚本中:
await program.rpc.withdrawUsdc(
amt2, nonce, {
accounts: {
programSigner,
authority: userKeys.publicKey,
usdcMint,
usdcUser,
usdcPgid,
dogeMint,
dogeUser,
dogePgid,
tokenProgram: TOKEN_PROGRAM_ID,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [userKeys],
});
上面的accounts对象,amt2,nonce值和stakeUsdc函数调用中的完全一样...所以不知道楼主哪里错了...
在失败之前,USDC 在 usdc_pgid 帐户上,该帐户来自:
usdcPgid = await createTokenAccount(provider, usdcMint, program.programId);//arguments: privider, mint, owner
usdc_pgid 的所有者是 program.programId,所以我应该让 cpi_accounts 使用 programId 作为权限...比如:
authority: ctx.program_id.to_account_info(),
错误[E0599]: 在当前范围内找不到名为 to_account_info
的方法供参考 &anchor_lang::prelude::Pubkey
--> programs/doge/src/lib.rs:265:33
|
265 |权限:ctx.program_id.to_account_info(),
| ^^^^^^^^^^^^^^^ 在 &anchor_lang::prelude::Pubkey
anchor_lang::CpiAccount https://docs.rs/anchor-lang/0.13.2/anchor_lang/struct.CpiAccount.html
anchor_lang::序曲::公钥 https://docs.rs/anchor-lang/0.13.2/anchor_lang/prelude/struct.Pubkey.html
从上面的文档来看,Pubkey类型没有将Pubkey类型转换为AccountInfo类型的方法! 我该怎么做?
程序的令牌余额账户必须有一个签名者 PDA 作为所有者!
所以token余额账户的所有者必须是programSigner!
锚代码:
programVault = await createTokenAccount(provider, usdcMint, programSigner);
第三个参数必须是programSigner!!!