Memo Transfer

What Is Memo Transfer?

The Token Extension Program's MemoTransfer account extension requires a memo instruction immediately before any incoming transfer to that token account. Outgoing transfers from that token account do not require a memo.

The extension is configured on the destination token account, not on the mint. Once enabled, transfers without a memo fail with TokenError::NoMemo until the token account owner disables the requirement.

How to Create a Token Account with MemoTransfer Enabled

To create a token account with MemoTransfer enabled:

  1. Create and initialize a mint.
  2. Calculate the token account size and rent needed for a token account with MemoTransfer.
  3. Create the token account and enable MemoTransfer on that account.
  4. Transfer and TransferChecked instructions to that token account must include a memo instruction immediately before the transfer instruction.

Create and initialize the mint

Create and initialize the mint that the token account will use.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);

Calculate token account size

Calculate the token account size for the base token account plus the MemoTransfer extension. This is the size used in CreateAccount.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const memoTransferAccountExtension = extension("MemoTransfer", {
requireIncomingTransferMemos: false
});
const tokenSpace = BigInt(getTokenSize([memoTransferAccountExtension]));

Calculate rent

Calculate rent using the token account size needed for the MemoTransfer extension.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const memoTransferAccountExtension = extension("MemoTransfer", {
requireIncomingTransferMemos: false
});
const tokenSpace = BigInt(getTokenSize([memoTransferAccountExtension]));
const tokenRent = await client.rpc
.getMinimumBalanceForRentExemption(tokenSpace)
.send();

Create and initialize the token account

Create the token account with the calculated space and lamports, then initialize it for the mint.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const memoTransferExtension = extension("MemoTransfer", {
requireIncomingTransferMemos: false
});
const tokenSpace = BigInt(getTokenSize([memoTransferExtension]));
const tokenRent = await client.rpc
.getMinimumBalanceForRentExemption(tokenSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: tokenAccount,
lamports: tokenRent,
space: tokenSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeAccountInstruction({
account: tokenAccount.address,
mint: mint.address,
owner: tokenOwner.address
})
]);

Enable MemoTransfer

Enable MemoTransfer on the token account.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const memoTransferExtension = extension("MemoTransfer", {
requireIncomingTransferMemos: false
});
const tokenSpace = BigInt(getTokenSize([memoTransferExtension]));
const tokenRent = await client.rpc
.getMinimumBalanceForRentExemption(tokenSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: tokenAccount,
lamports: tokenRent,
space: tokenSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeAccountInstruction({
account: tokenAccount.address,
mint: mint.address,
owner: tokenOwner.address
})
]);
await client.sendTransaction([
getEnableMemoTransfersInstruction({
token: tokenAccount.address,
owner: tokenOwner
})
]);

Create and initialize the mint

Create and initialize the mint that the token account will use.

Calculate token account size

Calculate the token account size for the base token account plus the MemoTransfer extension. This is the size used in CreateAccount.

Calculate rent

Calculate rent using the token account size needed for the MemoTransfer extension.

Create and initialize the token account

Create the token account with the calculated space and lamports, then initialize it for the mint.

Enable MemoTransfer

Enable MemoTransfer on the token account.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);

Source Reference

ItemDescriptionSource
MemoTransferAccount extension that stores whether incoming transfers require a memo.Source
RequiredMemoTransfersInstruction::EnableInstruction that enables memo-required transfers on a token account.Source
RequiredMemoTransfersInstruction::DisableInstruction that disables memo-required transfers on a token account.Source
process_toggle_required_memo_transfersProcessor logic that initializes or rewrites the MemoTransfer extension after verifying ownership.Source

Typescript

The Kit example below uses the generated instructions directly. Legacy examples using @solana/web3.js, @solana/spl-token, and @solana/spl-memo are included for reference.

Kit

Instructions
import {
lamports,
createClient,
generateKeyPairSigner,
unwrapOption
} from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import { getAddMemoInstruction } from "@solana-program/memo";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
extension,
fetchToken,
findAssociatedTokenPda,
getCreateAssociatedTokenInstructionAsync,
getDisableMemoTransfersInstruction,
getEnableMemoTransfersInstruction,
getInitializeAccountInstruction,
getInitializeMintInstruction,
getMintSize,
getMintToCheckedInstruction,
getTokenSize,
getTransferCheckedInstruction,
isExtension,
TOKEN_2022_PROGRAM_ADDRESS
} from "@solana-program/token-2022";
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const tokenOwner = await generateKeyPairSigner();
const tokenAccount = await generateKeyPairSigner();
const tokenAmount = 1n;
const mintSpace = BigInt(getMintSize());
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer, // Account funding the new mint account.
newAccount: mint, // New mint account to create.
lamports: mintRent, // Lamports funding the mint account rent.
space: mintSpace, // Account size in bytes for the mint.
programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.
}),
getInitializeMintInstruction({
mint: mint.address, // Mint account to initialize.
decimals: 0, // Number of decimals for the token.
mintAuthority: client.payer.address, // Authority allowed to mint new tokens.
freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.
})
]);
const [sourceToken] = await findAssociatedTokenPda({
mint: mint.address, // Mint for the source token account.
owner: client.payer.address, // Owner of the source token account.
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS // Token program that owns the token account.
});
const memoTransferAccountExtension = extension("MemoTransfer", {
requireIncomingTransferMemos: false
});
const tokenSpace = BigInt(getTokenSize([memoTransferAccountExtension]));
const tokenRent = await client.rpc
.getMinimumBalanceForRentExemption(tokenSpace)
.send();
await client.sendTransaction([
await getCreateAssociatedTokenInstructionAsync({
payer: client.payer, // Account funding the associated token account creation.
mint: mint.address, // Mint for the associated token account.
owner: client.payer.address // Owner of the associated token account.
}),
getMintToCheckedInstruction({
mint: mint.address, // Mint account that issues the tokens.
token: sourceToken, // Token account receiving the newly minted tokens.
mintAuthority: client.payer, // Signer authorized to mint new tokens.
amount: tokenAmount, // Token amount in base units.
decimals: 0 // Decimals defined on the mint.
}),
getCreateAccountInstruction({
payer: client.payer, // Account funding the new token account.
newAccount: tokenAccount, // New token account to create.
lamports: tokenRent, // Lamports funding the token account rent.
space: tokenSpace, // Account size in bytes for the token account plus MemoTransfer.
programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the token account.
}),
getInitializeAccountInstruction({
account: tokenAccount.address, // Token account to initialize.
mint: mint.address, // Mint for the token account.
owner: tokenOwner.address // Owner of the token account.
})
]);
await client.sendTransaction([
getEnableMemoTransfersInstruction({
token: tokenAccount.address, // Token account that stores the MemoTransfer extension.
owner: tokenOwner // Token account owner authorized to toggle memo requirements.
})
]);
await client.sendTransaction([
getAddMemoInstruction({
memo: "memo required" // Memo string required by the destination token account.
}),
getTransferCheckedInstruction({
source: sourceToken, // Token account sending the transfer.
mint: mint.address, // Mint for the transfer.
destination: tokenAccount.address, // Token account receiving the transfer.
authority: client.payer, // Owner of the source token account.
amount: tokenAmount, // Token amount in base units.
decimals: 0 // Decimals defined on the mint.
})
]);
await client.sendTransaction([
getDisableMemoTransfersInstruction({
token: tokenAccount.address, // Token account that stores the MemoTransfer extension.
owner: tokenOwner // Token account owner authorized to toggle memo requirements.
})
]);
const tokenAccountData = await fetchToken(client.rpc, tokenAccount.address);
const memoTransferExtension = (
unwrapOption(tokenAccountData.data.extensions) ?? []
).find((item) => isExtension("MemoTransfer", item));
console.log("Mint Address:", mint.address);
console.log("Token Account:", tokenAccount.address);
console.log("Destination Amount:", tokenAccountData.data.amount);
console.log("MemoTransfer Extension:", memoTransferExtension);
Console
Click to execute the code.

Web3.js

Instructions
import {
Connection,
Keypair,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
getAssociatedTokenAddressSync,
createAssociatedTokenAccountInstruction,
createDisableRequiredMemoTransfersInstruction,
createEnableRequiredMemoTransfersInstruction,
createInitializeAccountInstruction,
createInitializeMintInstruction,
getAccount,
getAccountLen,
getMemoTransfer,
createMintToCheckedInstruction,
createTransferCheckedInstruction,
ExtensionType,
getMintLen,
TOKEN_2022_PROGRAM_ID
} from "@solana/spl-token";
import { createMemoInstruction } from "@solana/spl-memo";
const connection = new Connection("http://localhost:8899", "confirmed");
const feePayer = Keypair.generate();
const tokenOwner = Keypair.generate();
const tokenAmount = 1;
const airdropSignature = await connection.requestAirdrop(
feePayer.publicKey,
5 * LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature, "confirmed");
const mint = Keypair.generate();
const mintLength = getMintLen([]);
const mintRent = await connection.getMinimumBalanceForRentExemption(mintLength);
const extensions = [ExtensionType.MemoTransfer];
const tokenAccount = Keypair.generate();
const tokenAccountLen = getAccountLen(extensions);
const tokenAccountRent =
await connection.getMinimumBalanceForRentExemption(tokenAccountLen);
const createMintAccountInstruction = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey, // Account funding the new mint account.
newAccountPubkey: mint.publicKey, // New mint account to create.
space: mintLength, // Account size in bytes for the mint.
lamports: mintRent, // Lamports funding the mint account rent.
programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
});
const initializeMintInstruction = createInitializeMintInstruction(
mint.publicKey, // Mint account to initialize.
0, // Number of decimals for the token.
feePayer.publicKey, // Authority allowed to mint new tokens.
feePayer.publicKey, // Authority allowed to freeze token accounts.
TOKEN_2022_PROGRAM_ID // Token program that owns the mint account.
);
const sourceToken = getAssociatedTokenAddressSync(
mint.publicKey, // Mint for the source token account.
feePayer.publicKey, // Owner of the source token account.
false, // Whether the owner is a PDA.
TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
ASSOCIATED_TOKEN_PROGRAM_ID // Associated Token Program that derives the ATA.
);
const createSourceTokenInstruction = createAssociatedTokenAccountInstruction(
feePayer.publicKey, // Account funding the associated token account creation.
sourceToken, // Associated token account address to create.
feePayer.publicKey, // Owner of the associated token account.
mint.publicKey, // Mint for the associated token account.
TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
ASSOCIATED_TOKEN_PROGRAM_ID // Associated Token Program that creates the account.
);
const mintToInstruction = createMintToCheckedInstruction(
mint.publicKey, // Mint account that issues the tokens.
sourceToken, // Token account receiving the newly minted tokens.
feePayer.publicKey, // Signer authorized to mint new tokens.
tokenAmount, // Token amount in base units.
0, // Decimals defined on the mint.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that owns the mint and token account.
);
const createTokenAccountInstruction = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey, // Account funding the new token account.
newAccountPubkey: tokenAccount.publicKey, // New token account to create.
space: tokenAccountLen, // Account size in bytes for the token account plus MemoTransfer.
lamports: tokenAccountRent, // Lamports funding the token account rent.
programId: TOKEN_2022_PROGRAM_ID // Program that owns the token account.
});
const initializeTokenAccountInstruction = createInitializeAccountInstruction(
tokenAccount.publicKey, // Token account to initialize.
mint.publicKey, // Mint for the token account.
tokenOwner.publicKey, // Owner of the token account.
TOKEN_2022_PROGRAM_ID // Token program that owns the token account.
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
createMintAccountInstruction,
initializeMintInstruction
),
[feePayer, mint]
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
createSourceTokenInstruction,
mintToInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction
),
[feePayer, tokenAccount]
);
const enableMemoTransferExtensionInstruction =
createEnableRequiredMemoTransfersInstruction(
tokenAccount.publicKey, // Token account that stores the MemoTransfer extension.
tokenOwner.publicKey, // Token account owner authorized to toggle memo requirements.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that owns the token account.
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(enableMemoTransferExtensionInstruction),
[feePayer, tokenOwner]
);
const memoIx = createMemoInstruction(
"memo required", // Memo string required by the destination token account.
[feePayer.publicKey] // Accounts to include in the memo instruction.
);
const transferInstruction = createTransferCheckedInstruction(
sourceToken, // Token account sending the transfer.
mint.publicKey, // Mint for the transfer.
tokenAccount.publicKey, // Token account receiving the transfer.
feePayer.publicKey, // Owner of the source token account.
tokenAmount, // Token amount in base units.
0, // Decimals defined on the mint.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that processes the transfer.
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(memoIx, transferInstruction),
[feePayer]
);
const disableMemoTransferExtensionInstruction =
createDisableRequiredMemoTransfersInstruction(
tokenAccount.publicKey, // Token account that stores the MemoTransfer extension.
tokenOwner.publicKey, // Token account owner authorized to toggle memo requirements.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that owns the token account.
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(disableMemoTransferExtensionInstruction),
[feePayer, tokenOwner]
);
const tokenAccountData = await getAccount(
connection,
tokenAccount.publicKey,
"confirmed",
TOKEN_2022_PROGRAM_ID
);
const memoTransferExtension = getMemoTransfer(tokenAccountData);
console.log("Mint Address:", mint.publicKey.toBase58());
console.log("Token Account:", tokenAccount.publicKey.toBase58());
console.log("Destination Amount:", tokenAccountData.amount.toString());
console.log("MemoTransfer Extension:", memoTransferExtension);
Console
Click to execute the code.

Rust

Rust
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
program_pack::Pack,
signature::{Keypair, Signer},
transaction::Transaction,
};
use solana_system_interface::instruction::create_account;
use spl_associated_token_account_interface::{
address::get_associated_token_address_with_program_id,
instruction::create_associated_token_account,
};
use spl_memo_interface::{instruction::build_memo, v3::ID as MEMO_PROGRAM_ID};
use spl_token_2022_interface::{
extension::{
memo_transfer::{
instruction::{disable_required_transfer_memos, enable_required_transfer_memos},
MemoTransfer,
},
BaseStateWithExtensions, ExtensionType, StateWithExtensions,
},
instruction::{initialize_account, initialize_mint, mint_to_checked, transfer_checked},
state::{Account, Mint},
ID as TOKEN_2022_PROGRAM_ID,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let fee_payer = Keypair::new();
let token_owner = Keypair::new();
let token_amount = 1u64;
let airdrop_signature = client
.request_airdrop(&fee_payer.pubkey(), 5_000_000_000)
.await?;
loop {
let confirmed = client.confirm_transaction(&airdrop_signature).await?;
if confirmed {
break;
}
}
let mint = Keypair::new();
let mint_space = Mint::LEN;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space)
.await?;
let create_mint_account_instruction = create_account(
&fee_payer.pubkey(), // Account funding the new mint account.
&mint.pubkey(), // New mint account to create.
mint_rent, // Lamports funding the mint account rent.
mint_space as u64, // Account size in bytes for the mint.
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
);
let initialize_mint_instruction = initialize_mint(
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
&mint.pubkey(), // Mint account to initialize.
&fee_payer.pubkey(), // Authority allowed to mint new tokens.
Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.
0, // Number of decimals for the token.
)?;
let create_mint_transaction = Transaction::new_signed_with_payer(
&[create_mint_account_instruction, initialize_mint_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&create_mint_transaction)
.await?;
let source_token = get_associated_token_address_with_program_id(
&fee_payer.pubkey(), // Owner of the associated token account.
&mint.pubkey(), // Mint for the associated token account.
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
);
let create_source_token_instruction = create_associated_token_account(
&fee_payer.pubkey(), // Account funding the associated token account creation.
&fee_payer.pubkey(), // Owner of the associated token account.
&mint.pubkey(), // Mint for the associated token account.
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
);
let mint_to_instruction = mint_to_checked(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint and token account.
&mint.pubkey(), // Mint account that issues the tokens.
&source_token, // Token account receiving the newly minted tokens.
&fee_payer.pubkey(), // Signer authorized to mint new tokens.
&[], // Additional multisig signers.
token_amount, // Token amount in base units.
0, // Decimals defined on the mint.
)?;
let token_account = Keypair::new();
let token_account_space =
ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::MemoTransfer])?;
let token_account_rent = client
.get_minimum_balance_for_rent_exemption(token_account_space)
.await?;
let create_token_account_instruction = create_account(
&fee_payer.pubkey(), // Account funding the new token account.
&token_account.pubkey(), // New token account to create.
token_account_rent, // Lamports funding the token account rent.
token_account_space as u64, // Account size in bytes for the token account plus MemoTransfer.
&TOKEN_2022_PROGRAM_ID, // Program that owns the token account.
);
let initialize_token_account = initialize_account(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
&token_account.pubkey(), // Token account to initialize.
&mint.pubkey(), // Mint for the token account.
&token_owner.pubkey(), // Owner of the token account.
)?;
let setup_transaction = Transaction::new_signed_with_payer(
&[
create_source_token_instruction,
mint_to_instruction,
create_token_account_instruction,
initialize_token_account,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &token_account],
client.get_latest_blockhash().await?,
);
client.send_and_confirm_transaction(&setup_transaction).await?;
let enable_memo_transfer_instruction = enable_required_transfer_memos(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
&token_account.pubkey(), // Token account that stores the MemoTransfer extension.
&token_owner.pubkey(), // Token account owner authorized to toggle memo requirements.
&[], // Additional multisig signers.
)?;
let enable_blockhash = client.get_latest_blockhash().await?;
let enable_transaction = Transaction::new_signed_with_payer(
&[enable_memo_transfer_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer, &token_owner],
enable_blockhash,
);
client
.send_and_confirm_transaction(&enable_transaction)
.await?;
let memo_instruction = build_memo(
&MEMO_PROGRAM_ID, // Memo program that records the memo string.
b"memo required", // Memo string required by the destination token account.
&[&fee_payer.pubkey()], // Accounts to include in the memo instruction.
);
let transfer_instruction = transfer_checked(
&TOKEN_2022_PROGRAM_ID, // Token program that processes the transfer.
&source_token, // Token account sending the transfer.
&mint.pubkey(), // Mint for the transfer.
&token_account.pubkey(), // Token account receiving the transfer.
&fee_payer.pubkey(), // Owner of the source token account.
&[], // Additional multisig signers.
token_amount, // Token amount in base units.
0, // Decimals defined on the mint.
)?;
let transfer_blockhash = client.get_latest_blockhash().await?;
let transfer_transaction = Transaction::new_signed_with_payer(
&[memo_instruction, transfer_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer],
transfer_blockhash,
);
client
.send_and_confirm_transaction(&transfer_transaction)
.await?;
let disable_memo_transfer_instruction = disable_required_transfer_memos(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
&token_account.pubkey(), // Token account that stores the MemoTransfer extension.
&token_owner.pubkey(), // Token account owner authorized to toggle memo requirements.
&[], // Additional multisig signers.
)?;
let disable_blockhash = client.get_latest_blockhash().await?;
let disable_transaction = Transaction::new_signed_with_payer(
&[disable_memo_transfer_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer, &token_owner],
disable_blockhash,
);
client
.send_and_confirm_transaction(&disable_transaction)
.await?;
let token_account_data = client.get_account(&token_account.pubkey()).await?;
let token_account_state = StateWithExtensions::<Account>::unpack(&token_account_data.data)?;
let memo_transfer_extension = token_account_state.get_extension::<MemoTransfer>()?;
println!("Mint Address: {}", mint.pubkey());
println!("Token Account: {}", token_account.pubkey());
println!("Destination Amount: {}", token_account_state.base.amount);
println!("MemoTransfer Extension: {:#?}", memo_transfer_extension);
Ok(())
}
Console
Click to execute the code.

Is this page helpful?

Table of Contents

Edit Page

Managed by

© 2026 Solana Foundation.
All rights reserved.
Get connected