Skip to main content
added 1177 characters in body
Source Link
john
  • 7.8k
  • 2
  • 10
  • 26

Additionally, you'll also need an instruction in your program to create a Token Account. In the reference below, the PDA is both the address of the Token Account and the authority of the token account.

// create PDA token account
use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, Token, TokenAccount};
#[derive(Accounts)]
pub struct Init<'info> {
    #[account(
        init,
        payer = payer,
        token::mint = mint,
        token::authority = auth,
        seeds = ["auth".as_bytes().as_ref()],
        bump,
    )]
    pub token_account: Account<'info, TokenAccount>,
    pub mint: Account<'info, Mint>,
    pub token_program: Program<'info, Token>,
    pub system_program: Program<'info, System>,
    pub rent: Sysvar<'info, Rent>,
    /// CHECK: PDA
    #[account(
        seeds = ["auth".as_bytes().as_ref()],
        bump,
    )]
    pub auth: UncheckedAccount<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
}

pub fn init_handler(_ctx: Context<Init>) -> Result<()> {
    Ok(())
}

Additionally, you'll also need an instruction in your program to create a Token Account. In the reference below, the PDA is both the address of the Token Account and the authority of the token account.

// create PDA token account
use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, Token, TokenAccount};
#[derive(Accounts)]
pub struct Init<'info> {
    #[account(
        init,
        payer = payer,
        token::mint = mint,
        token::authority = auth,
        seeds = ["auth".as_bytes().as_ref()],
        bump,
    )]
    pub token_account: Account<'info, TokenAccount>,
    pub mint: Account<'info, Mint>,
    pub token_program: Program<'info, Token>,
    pub system_program: Program<'info, System>,
    pub rent: Sysvar<'info, Rent>,
    /// CHECK: PDA
    #[account(
        seeds = ["auth".as_bytes().as_ref()],
        bump,
    )]
    pub auth: UncheckedAccount<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
}

pub fn init_handler(_ctx: Context<Init>) -> Result<()> {
    Ok(())
}
Source Link
john
  • 7.8k
  • 2
  • 10
  • 26

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(())
}