Here is a simple example using the CpiContext::new_with_signer to transfer some tokens from a token vault owned by this program. Inside the seeds I put in the values needed to create the PDA of the vault as described in the Withdraw vault_account seeds.
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(
mut,
seeds = [b"vault", to.mint.as_ref()],
bump,
)]
pub vault_account: Account<'info, TokenAccount>,
#[account(
mut,
seeds = [b"vault-access", to.mint.as_ref(), authority.key().as_ref()],
bump,
constraint = vault_access.authority == authority.key()
)]
pub vault_access: Account<'info, VaultAccess>,
#[account(mut, constraint = to.owner == authority.key())]
pub to: Account<'info, TokenAccount>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
pub fn withdraw(ctx: Context<Withdraw>, amount: u64, bump: u8) -> Result<()> {
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_accounts = Transfer {
from: ctx.accounts.vault_account.to_account_info(),
to: ctx.accounts.to.to_account_info(),
authority: ctx.accounts.vault_account.to_account_info(),
};
if amount > ctx.accounts.vault_access.amount {
return Err(ErrorCode::InsufficientFundsInVault.into())
}
ctx.accounts.vault_access.amount -= amount;
token::transfer(
CpiContext::new_with_signer(
cpi_program,
cpi_accounts,
&[&[b"vault", ctx.accounts.to.mint.as_ref(), &[bump]]]),
amount)?;
Ok(())
}
Note - with the latest version of anchor you don't need to pass the bump in as a parameter if you don't want to. There are new ways to deal with the bump that can make things a bit easier.