To transfer an SPL token from a Token account controlled by a PDA of your program, the PDA will need to sign the transfer instruction.
In the example below, auth
represents the PDA with authority over the token account.
#[account(
seeds = ["auth".as_bytes().as_ref()],
bump,
)]
pub auth: UncheckedAccount<'info>,
And is used to sign the transfer instruction, note to use CpiContext::new_with_signer
pub fn transfer_handler(ctx: Context<TokenTransfer>) -> Result<()> {
let seeds = &["auth".as_bytes(), &[*ctx.bumps.get("auth").unwrap()]];
let signer = [&seeds[..]];
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from: ctx.accounts.from_token_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
authority: ctx.accounts.auth.to_account_info(),
},
&signer,
);
token::transfer(cpi_ctx, 1)?;
Ok(())
}
Here is reference:
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token::{self, Mint, Token, TokenAccount},
};
#[derive(Accounts)]
pub struct TokenTransfer<'info> {
/// CHECK: token account authority PDA
#[account(
seeds = ["auth".as_bytes().as_ref()],
bump,
)]
pub auth: UncheckedAccount<'info>,
#[account(
mut,
token::mint = mint,
token::authority = auth
)]
pub from_token_account: Account<'info, TokenAccount>,
#[account(
init_if_needed,
payer = payer,
associated_token::mint = mint,
associated_token::authority = payer
)]
pub to_token_account: Account<'info, TokenAccount>,
pub mint: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}
// transfer SPL token from PDA token account to user token account
pub fn transfer_handler(ctx: Context<TokenTransfer>) -> Result<()> {
let seeds = &["auth".as_bytes(), &[*ctx.bumps.get("auth").unwrap()]];
let signer = [&seeds[..]];
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from: ctx.accounts.from_token_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
authority: ctx.accounts.auth.to_account_info(),
},
&signer,
);
token::transfer(cpi_ctx, 1)?;
Ok(())
}