Muistion siirto

Miten MemoTransferExtension otetaan käyttöön

MemoTransferExtension vaatii, että kaikkiin saapuviin token-siirtoihin sisältyy liitteenä muistio, kun se on otettu käyttöön token account -tilillä. Token account -tilin omistaja voi ottaa tämän laajennuksen käyttöön tai poistaa sen käytöstä.

Typescript

import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners
} from "@solana/kit";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
getInitializeMintInstruction,
getMintSize,
TOKEN_2022_PROGRAM_ADDRESS,
extension,
getTokenSize,
findAssociatedTokenPda,
getCreateAssociatedTokenInstructionAsync,
getEnableMemoTransfersInstruction,
getInitializeAccountInstruction,
getMintToInstruction,
getTransferCheckedInstruction
} from "@solana-program/token-2022";
import { getAddMemoInstruction } from "@solana-program/memo";
// Create Connection, local validator in this example
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate the authority for the mint, (will also act as the fee payer)
const authority = await generateKeyPairSigner();
// Fund authority/fee payer
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: authority.address,
lamports: lamports(5_000_000_000n), // 5 SOL
commitment: "confirmed"
});
// Generate keypair to use as address of mint
const mint = await generateKeyPairSigner();
// Get default mint account size (in bytes),
const space = BigInt(getMintSize());
// Get minimum balance for rent exemption
const rent = await rpc.getMinimumBalanceForRentExemption(space).send();
// Get latest blockhash to include in transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Instruction to create new account for mint (token program)
// Invokes the system program
const createMintAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: mint,
lamports: rent,
space,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
});
// Instruction to initialize mint account data
// Invokes the token22 program
const initializeMintInstruction = getInitializeMintInstruction({
mint: mint.address,
decimals: 9,
mintAuthority: authority.address
});
// Use findAssociatedTokenPda to derive the ATA address
const [associatedTokenAddress] = await findAssociatedTokenPda({
mint: mint.address,
owner: authority.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
// instruction to create the associated token account for the authority
const createAtaInstruction = await getCreateAssociatedTokenInstructionAsync({
payer: authority,
mint: mint.address,
owner: authority.address
});
// Create instruction to mint tokens
const mintToInstruction = getMintToInstruction({
mint: mint.address,
token: associatedTokenAddress,
mintAuthority: authority.address,
amount: 1000_000_000_000n // 1000.0 tokens with 9 decimals
});
// Generate keypair to use as address of token account
const tokenAccount = await generateKeyPairSigner();
// memo transfer extension.
const memoTransferExtension = extension("MemoTransfer", {
requireIncomingTransferMemos: true
});
// get token account size with extension enabled
const tokenAccountLen = BigInt(getTokenSize([memoTransferExtension]));
// Get minimum balance for rent exemption
const tokenAccountRent = await rpc
.getMinimumBalanceForRentExemption(tokenAccountLen)
.send();
// Instruction to create new account for the token account
// Invokes the system program
const createTokenAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: tokenAccount,
lamports: tokenAccountRent,
space: tokenAccountLen,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
});
// Instruction to initialize the created token account
const initializeTokenAccountInstruction = getInitializeAccountInstruction({
account: tokenAccount.address,
mint: mint.address,
owner: authority.address
});
// create instruction to enable the MemoTransferExtension
const enableMemoTransferExtensionInstruction =
getEnableMemoTransfersInstruction({
token: tokenAccount.address,
owner: authority
});
const instructions = [
createMintAccountInstruction,
initializeMintInstruction,
createAtaInstruction,
mintToInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction,
enableMemoTransferExtensionInstruction
];
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(authority, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions(instructions, tx)
);
// Sign transaction message with all required signers
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed", skipPreflight: true }
);
// // Get transaction signature
const transactionSignature = getSignatureFromTransaction(signedTransaction);
console.log("Mint Address:", mint.address.toString());
console.log("Transaction Signature:", transactionSignature);
// Get a fresh blockhash for the transfer with memo enabled
const { value: latestBlockhash2 } = await rpc.getLatestBlockhash().send();
// construct memo instruction
const transferMemoInstruction = getAddMemoInstruction({
memo: "This transfer requires a memo to accompany it"
});
// create transfer insruction
const transferInstruction = getTransferCheckedInstruction({
source: associatedTokenAddress,
mint: mint.address,
destination: tokenAccount.address,
authority: authority,
amount: 1_000_000_000,
decimals: 9
});
// construct transaction message for transfer
const transferMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(authority, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash2, tx),
(tx) =>
appendTransactionMessageInstructions(
[transferMemoInstruction, transferInstruction],
tx
)
);
// Sign transaction message with all required signers
const signedTransferTx =
await signTransactionMessageWithSigners(transferMessage);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransferTx,
{ commitment: "confirmed" }
);
// Get transaction signature
const transferTxSig = getSignatureFromTransaction(signedTransferTx);
console.log("\nTransferred tokens with memo ix enabled");
console.log("Transaction Signature:", transferTxSig);
Console
Click to execute the code.

Rust

Rust
use anyhow::Result;
use solana_client::{nonblocking::rpc_client::RpcClient, rpc_config::RpcTransactionConfig};
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
program_pack::Pack,
signature::{Keypair, Signer},
transaction::Transaction,
};
use solana_system_interface::instruction::create_account;
use solana_transaction_status_client_types::{
option_serializer::OptionSerializer, UiTransactionEncoding,
};
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::enable_required_transfer_memos, MemoTransfer},
BaseStateWithExtensions, ExtensionType, StateWithExtensions,
},
instruction::{initialize_account, initialize_mint, mint_to, transfer_checked},
state::{Account, Mint},
ID as TOKEN_2022_PROGRAM_ID,
};
#[tokio::main]
async fn main() -> Result<()> {
// Create connection to local validator
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let latest_blockhash = client.get_latest_blockhash().await?;
// Generate a new keypair for the fee payer
let fee_payer = Keypair::new();
// Airdrop 5 SOL to fee payer
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;
}
}
// Generate keypair to use as address of mint
let mint = Keypair::new();
// Get default mint account size (in bytes), no extensions enabled
let mint_space = Mint::LEN;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space)
.await?;
// Instruction to create new account for mint (token22)
let create_mint_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&mint.pubkey(), // new account (mint)
mint_rent, // lamports
mint_space as u64, // space
&TOKEN_2022_PROGRAM_ID, // program id
);
// Instruction to initialize mint account data
let initialize_mint_instruction = initialize_mint(
&TOKEN_2022_PROGRAM_ID, // program id
&mint.pubkey(), // mint
&fee_payer.pubkey(), // mint authority
Some(&fee_payer.pubkey()), // freeze authority
9, // decimals
)?;
// Calculate the associated token account address for fee_payer
let associated_token_address = get_associated_token_address_with_program_id(
&fee_payer.pubkey(), // owner
&mint.pubkey(), // mint
&TOKEN_2022_PROGRAM_ID, // program_id
);
// Instruction to create associated token account
let create_ata_instruction = create_associated_token_account(
&fee_payer.pubkey(), // funding address
&fee_payer.pubkey(), // wallet address
&mint.pubkey(), // mint address
&TOKEN_2022_PROGRAM_ID, // program id
);
// Amount of tokens to mint (100 tokens with 9 decimals)
let amount = 100_000_000_000;
// Create mint_to instruction to mint tokens to the associated token account
let mint_to_instruction = mint_to(
&TOKEN_2022_PROGRAM_ID, // program_id
&mint.pubkey(), // mint
&associated_token_address, // destination
&fee_payer.pubkey(), // authority
&[&fee_payer.pubkey()], // signer
amount, // amount
)?;
// Generate keypair to use as address of token account
let token_account = Keypair::new();
// Get default token account size (in bytes), with memo transfer extension enabled
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?;
// Instruction to create new account for token account (token22)
let create_token_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&token_account.pubkey(), // new account (token account)
token_account_rent, // rent
token_account_space as u64, // space
&TOKEN_2022_PROGRAM_ID, // program id
);
// initialize token account
let initialize_token_account = initialize_account(
&TOKEN_2022_PROGRAM_ID, // program_id
&token_account.pubkey(), // token account
&mint.pubkey(), // mint
&fee_payer.pubkey(), // authority
)?;
// enable memo transfer extension
let enable_memo_transfer_instruction = enable_required_transfer_memos(
&TOKEN_2022_PROGRAM_ID, // program_id
&token_account.pubkey(), // token account
&fee_payer.pubkey(), // authority
&[&fee_payer.pubkey()], // authority
)?;
// Construct transaction with previous instructions
let transaction = Transaction::new_signed_with_payer(
&[
create_mint_account_instruction,
initialize_mint_instruction,
create_ata_instruction,
mint_to_instruction,
create_token_account_instruction,
initialize_token_account,
enable_memo_transfer_instruction,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint, &token_account],
latest_blockhash,
);
// Send and confirm transaction
client.send_and_confirm_transaction(&transaction).await?;
println!(
"Token Account with memo transfer enabled: {}",
token_account.pubkey()
);
println!("\nSuccessfully enabled memo transfer extension on token account");
// Fetch and deserialize the token account with memo transfer extension
let token_account_data = client.get_account(&token_account.pubkey()).await?;
let token_account_state = StateWithExtensions::<Account>::unpack(&token_account_data.data)?;
// Get all extension types enabled on this token account
let extension_types = token_account_state.get_extension_types()?;
println!("\nToken Account Extensions enabled: {:?}", extension_types);
// Deserialize the MemoTransfer extension data
let memo_transfer = token_account_state.get_extension::<MemoTransfer>()?;
println!("\n{:#?}", memo_transfer);
// Create transfer_checked instruction to send tokens from source to destination
let transfer_instruction = transfer_checked(
&TOKEN_2022_PROGRAM_ID, // program id
&associated_token_address, // source
&mint.pubkey(), // mint
&token_account.pubkey(), // destination
&fee_payer.pubkey(), // owner of source
&[&fee_payer.pubkey()], // signers
1_000_000_000, // amount
9, // decimals
)?;
let memo = String::from("This transfer requires a memo to accompany it");
let memo_instruction = build_memo(&MEMO_PROGRAM_ID, memo.as_bytes(), &[&fee_payer.pubkey()]);
// Create transaction for approving delegate
let transaction = Transaction::new_signed_with_payer(
&[memo_instruction, transfer_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer],
latest_blockhash,
);
// Send and confirm transaction
let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
println!("\nSuccessfully transferred tokens with memo transfer extension enabled");
println!("Transaction Signature: {}", transaction_signature);
// Fetch the transfer transaction details
let transfer_transaction_details = client
.get_transaction_with_config(
&transaction_signature,
RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Json),
commitment: Some(CommitmentConfig::confirmed()),
max_supported_transaction_version: Some(0),
},
)
.await?;
// Log transfer transaction logs
if let Some(meta) = transfer_transaction_details.transaction.meta {
if let OptionSerializer::Some(log_messages) = meta.log_messages {
println!("\nTransaction Logs:");
for log in log_messages {
println!(" {}", log);
}
}
}
Ok(())
}
Console
Click to execute the code.

Is this page helpful?

Sisällysluettelo

Muokkaa sivua

Hallinnoi

© 2025 Solana Foundation.
Kaikki oikeudet pidätetään.