Memo-Transfer

Was ist Memo-Transfer?

Die MemoTransfer-Kontoerweiterung des Token Extensions Program erfordert eine Memo-Anweisung unmittelbar vor jedem eingehenden Transfer auf dieses Token-Konto. Ausgehende Transfers von diesem Token-Konto erfordern kein Memo.

Die Erweiterung wird auf dem Ziel-Token-Konto konfiguriert, nicht auf dem Mint. Sobald aktiviert, schlagen Transfers ohne Memo mit TokenError::NoMemo fehl, bis der Token-Kontoinhaber die Anforderung deaktiviert.

So erstellen Sie ein Token-Konto mit aktiviertem MemoTransfer

So erstellen Sie ein Token-Konto mit aktiviertem MemoTransfer:

  1. Erstellen und initialisieren Sie einen Mint.
  2. Berechnen Sie die Token-Kontogröße und die benötigte Miete für ein Token-Konto mit MemoTransfer.
  3. Erstellen Sie das Token-Konto und aktivieren Sie MemoTransfer auf diesem Konto.
  4. Transfer- und TransferChecked- Anweisungen auf dieses Token-Konto müssen eine Memo-Anweisung unmittelbar vor der Transfer-Anweisung enthalten.

Mint erstellen und initialisieren

Erstellen und initialisieren Sie den Mint, den das Token-Konto verwenden wird.

Token-Kontogröße berechnen

Berechnen Sie die Token-Kontogröße für das Basis-Token-Konto zuzüglich der MemoTransfer-Erweiterung. Dies ist die Größe, die in CreateAccount verwendet wird.

Miete berechnen

Berechnen Sie die Miete unter Verwendung der Token-Kontogröße, die für die MemoTransfer-Erweiterung benötigt wird.

Erstellen und Initialisieren des Token-Kontos

Erstellen Sie das Token-Konto mit dem berechneten Speicherplatz und den Lamports und initialisieren Sie es dann für die Mint.

MemoTransfer aktivieren

Aktivieren Sie MemoTransfer auf dem Token-Konto.

Mint erstellen und initialisieren

Erstellen und initialisieren Sie den Mint, den das Token-Konto verwenden wird.

Token-Kontogröße berechnen

Berechnen Sie die Token-Kontogröße für das Basis-Token-Konto zuzüglich der MemoTransfer-Erweiterung. Dies ist die Größe, die in CreateAccount verwendet wird.

Miete berechnen

Berechnen Sie die Miete unter Verwendung der Token-Kontogröße, die für die MemoTransfer-Erweiterung benötigt wird.

Erstellen und Initialisieren des Token-Kontos

Erstellen Sie das Token-Konto mit dem berechneten Speicherplatz und den Lamports und initialisieren Sie es dann für die Mint.

MemoTransfer aktivieren

Aktivieren Sie MemoTransfer auf dem Token-Konto.

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
})
]);

Quellenreferenz

ElementBeschreibungQuelle
MemoTransferKontenerweiterung, die speichert, ob eingehende Überweisungen ein Memo erfordern.Quelle
RequiredMemoTransfersInstruction::EnableAnweisungen, die Überweisungen mit Memo-Pflicht auf einem Token-Konto aktivieren.Quelle
RequiredMemoTransfersInstruction::DisableAnweisungen, die Überweisungen mit Memo-Pflicht auf einem Token-Konto deaktivieren.Quelle
process_toggle_required_memo_transfersProzessorlogik, die die MemoTransfer-Erweiterung initialisiert oder neu schreibt, nachdem die Eigentümerschaft überprüft wurde.Quelle

Typescript

Das Beispiel Kit unten verwendet die generierten Anweisungen direkt. Legacy-Beispiele mit @solana/web3.js, @solana/spl-token und @solana/spl-memo sind als Referenz enthalten.

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?

Inhaltsverzeichnis

Seite bearbeiten

Verwaltet von

© 2026 Solana Foundation.
Alle Rechte vorbehalten.
Verbinden Sie sich