Ekstensi Memo Transfer
Ekstensi MemoTranfer
adalah ekstensi akun Token
yang mewajibkan semua transfer masuk ke akun token menyertakan memo, memfasilitasi pelacakan transaksi yang lebih baik dan identifikasi pengguna.
Initializing the Token Account
Karena Anchor
tidak memiliki makro untuk ekstensi memo_transfer
, kita akan membuat akun Token
menggunakan CPI mentah.
Berikut cara membuat token dengan ekstensi Memo Transfer:
use anchor_lang::prelude::*;
use anchor_lang::system_program::{create_account, CreateAccount};
use anchor_spl::{
token_2022::{
initialize_account3,
spl_token_2022::{extension::ExtensionType, pod::PodAccount},
InitializeAccount3,
},
token_interface::{memo_transfer_initialize, MemoTransfer, Mint, Token2022},
};
// There is currently not an anchor constraint to automatically initialize the MemoTransfer extension
// We can manually create and initialize the token account via CPIs in the instruction handler
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// Calculate space required for token and extension data
let token_account_size =
ExtensionType::try_calculate_account_len::<PodAccount>(&[ExtensionType::MemoTransfer])?;
// Calculate minimum lamports required for size of token account with extensions
let lamports = (Rent::get()?).minimum_balance(token_account_size);
// Invoke System Program to create new account with space for token account and extension data
create_account(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
CreateAccount {
from: ctx.accounts.payer.to_account_info(),
to: ctx.accounts.token_account.to_account_info(),
},
),
lamports, // Lamports
token_account_size as u64, // Space
&ctx.accounts.token_program.key(), // Owner Program
)?;
// Initialize the standard token account data
initialize_account3(CpiContext::new(
ctx.accounts.token_program.to_account_info(),
InitializeAccount3 {
account: ctx.accounts.token_account.to_account_info(),
mint: ctx.accounts.mint_account.to_account_info(),
authority: ctx.accounts.payer.to_account_info(),
},
))?;
// Initialize the memo transfer extension
// This instruction must come after the token account initialization
memo_transfer_initialize(CpiContext::new(
ctx.accounts.token_program.to_account_info(),
MemoTransfer {
token_program_id: ctx.accounts.token_program.to_account_info(),
account: ctx.accounts.token_account.to_account_info(),
owner: ctx.accounts.payer.to_account_info(),
},
))?;
Ok(())
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut)]
pub token_account: Signer<'info>,
pub mint_account: InterfaceAccount<'info, Mint>,
pub token_program: Program<'info, Token2022>,
pub system_program: Program<'info, System>,
}
Transferring Token with a Memo
Untuk menggunakan program Memo di Solana, kita memiliki dua jalur yang memungkinkan.
Membangun instruksi memo "mentah" kita sendiri seperti ini:
const message = "Hello, Solana";
new TransactionInstruction({
keys: [{ pubkey: keypair.publicKey, isSigner: true, isWritable: true }],
data: Buffer.from(message, "utf-8"), // Memo message. In this case it is "Hello, Solana"
programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"), // Memo program that validates keys and memo message
}),
Atau kita dapat menggunakan SDK program Memo setelah mengunduh paketnya seperti ini:
npm i @solana/spl-memo
Dalam contoh kita, kita akan menggunakan opsi kedua dan akan terlihat seperti ini:
const memoInstruction = createMemoInstruction(
"Hello, world!",
[keypair.publicKey],
);
//...anchor instruction with the transfer instruction
const transferTransaction = new Transaction().add(
memoInstruction,
transferInstruction,
);
const transferSignature = await sendAndConfirmTransaction(connection, transferTransaction, [keypair]);
console.log(`Tokens transferred with memo! Check out your TX here: https://explorer.solana.com/tx/${transferSignature}?cluster=devnet`);
Disabling and Enabling the Memo
Jika kita tidak ingin mewajibkan transfer disertai dengan memo, kita dapat melakukannya menggunakan fungsi memo_transfer_disable
dan akan terlihat seperti ini:
use anchor_lang::prelude::*;
use anchor_spl::token_interface::{memo_transfer_disable, MemoTransfer, Token, Mint, Token2022};
#[derive(Accounts)]
pub struct DisableMemo<'info> {
#[account(mut)]
pub owner: Signer<'info>,
#[account(
mut,
token::authority = owner,
)]
pub token_account: InterfaceAccount<'info, TokenAccount>,
pub token_program: Program<'info, Token2022>,
}
pub fn disable_memo(
ctx: Context<DisableMemo>,
) -> Result<()> {
memo_transfer_disable(CpiContext::new(
ctx.accounts.token_program.to_account_info(),
MemoTransfer {
token_program_id: ctx.accounts.token_program.to_account_info(),
account: ctx.accounts.token_account.to_account_info(),
owner: ctx.accounts.owner.to_account_info(),
},
))?;
Ok(())
}