Memo Transfer Extension
MemoTranfer
Extension là một Token
account extension thực thi rằng tất cả transfer đến một token account phải bao gồm memo, tạo điều kiện cho việc theo dõi giao dịch nâng cao và nhận dạng người dùng.
Khởi tạo Token Account
Vì Anchor
không có bất kỳ macro nào cho phần mở rộng memo_transfer
, chúng ta sẽ tạo Token
account bằng cách sử dụng một CPI thô.
Đây là cách tạo token với phần mở rộng 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>,
}
Transfer Token với Memo
Để sử dụng Memo program trên Solana, chúng ta có hai cách khả thi.
Xây dựng memo instruction "thô" của riêng chúng ta như thế này:
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
}),
Hoặc chúng ta có thể sử dụng Memo program SDK sau khi tải gói hỗ trợ như thế này:
npm i @solana/spl-memo
> Sau khi tạo memo, instruction tiếp theo sẽ là một instruction chuyển token đơn giản.
Trong ví dụ của chúng ta, chúng ta sẽ sử dụng cách thứ hai và nó sẽ trông như thế này:
```ts
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`);
Vô hiệu hóa và kích hoạt Memo
Nếu chúng ta không muốn thực thi transfer phải đi kèm với memo, chúng ta có thể làm như vậy bằng cách sử dụng hàm memo_transfer_disable
và nó sẽ trông như thế này:
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(())
}