Token transfer
Outline
It is a necessary operation to transfer tokens between accounts. Use transfer
instruction of token
program to perform transfer operation.
The final outcome of this chapter could be found here
How to transfer a token to other account?
So far, we have created a mint and minted it to an account. But how to transfer the mint to other accounts?
It also involves the same process. Create a TransferTokenToAnother
context and create an instruction transfer_token_to_another
to achieve the end goal.
How to create a TransferTokenToAnother
context?
Below is the struct
that we define for transferring a spl_token_mint
token to other account.
// Transfer token to another account
#[derive(Accounts)]
pub struct TransferTokenToAnother<'info> {
#[account(
seeds = [
b"spl-token-mint".as_ref(),
],
bump = vault.spl_token_mint_bump,
)]
pub spl_token_mint: Account<'info, Mint>, // ---> 1
#[account(
seeds = [
b"vault"
],
bump = vault.bump, // --> 2
)]
pub vault: Account<'info, Vault>,
#[account(
mut,
associated_token::mint = spl_token_mint,
associated_token::authority = payer
)]
pub payer_mint_ata: Box<Account<'info, TokenAccount>>, // --> 3
#[account(mut)]
pub payer: Signer<'info>, // ---> 4
pub system_program: Program<'info, System>, // ---> 5
pub token_program: Program<'info, Token>, // ---> 6
pub rent: Sysvar<'info, Rent>, // ---> 7
pub associated_token_program: Program<'info, AssociatedToken>, // ---> 8
#[account(
init,
payer = payer,
associated_token::mint = spl_token_mint,
associated_token::authority = another_account
)]
pub another_mint_ata: Box<Account<'info, TokenAccount>>, // --> 9
/// CHECK : We just pass the account info for the demonstration purpose. Ideally this is either signer or trusted account
pub another_account: AccountInfo<'info>, // ---> 10
}
- We pass the
spl_token_mint
account without anymut
orinit
decoration. - We pass the
vault
. This can be used for security purpose. payer_mint_ata
account from which we are transferring token to another atapayer
signer who is transferring the tokensystem_program
account for executing the instruction.token_program
account used for performingtransfer
operationinit
decorator usesrent
account for creating account- We are creating a new ata account. Hence we pass
associated_token_program
another_mint_ata
account to which we transfer the token.another_account
account is the owner ofanother_mint_ata
Create a transfer_token_to_another
instruction for transferring a token.
pub fn transfer_token_to_another(ctx : Context<TransferTokenToAnother>) -> Result<()> {
let cpi_context = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from : ctx.accounts.payer_mint_ata.to_account_info(),
to : ctx.accounts.another_mint_ata.to_account_info(),
authority : ctx.accounts.payer.to_account_info()
},
);
token::transfer(cpi_context, 1)?;
Ok(())
}
Time to test the transfer_token_to_another
instruction.
Let us first create anotherWallet and add it to the root of the test case.
const anotherWallet = anchor.web3.Keypair.generate(); // newly created another wallet
And add some sols to the wallet using addSols
function in before
function block.
before("Add sols to wallet ", async () => {
await addSols(provider, payer.publicKey);
await addSols(provider, anotherWallet.publicKey); // add sols to another wallet
});
We will test case in spl-token.ts
. Add the following in your test file.
it("should transfer 1 token from payer_mint_ata to another_mint_ata", async () => {
try {
const [splTokenMint, _1] = await findSplTokenMintAddress();
const [vaultMint, _2] = await findVaultAddress();
const [payerMintAta, _3] = await findAssociatedTokenAccount(
payer.publicKey,
splTokenMint
);
const [anotherMintAta, _4] = await findAssociatedTokenAccount(
anotherWallet.publicKey,
splTokenMint
);
const tx = await program.methods
.transferTokenToAnother()
.accounts({
splTokenMint: splTokenMint,
vault: vaultMint,
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
tokenProgram: TOKEN_PROGRAM_ID,
systemProgram: SystemProgram.programId,
payerMintAta: payerMintAta,
payer: payer.publicKey,
anotherMintAta: anotherMintAta,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
anotherAccount: anotherWallet.publicKey,
})
.signers([payer])
.rpc();
console.log("Your transaction signature", tx);
} catch (err) {
console.log(err);
}
});
Run the command
anchor test
Then the output should be a success as shown below.
So, far we have understood how minting and transfer work. In the next sections we will discuss about freezing a token account.