Розширення Memo Transfer
Розширення MemoTranfer
є розширенням облікового запису Token
, яке забезпечує, щоб усі вхідні перекази на токен-рахунок містили примітку, що сприяє покращеному відстеженню транзакцій та ідентифікації користувачів.
Initializing the Token Account
Оскільки Anchor
не має макросів для розширення memo_transfer
, ми створимо обліковий запис Token
за допомогою необроблених CPI.
Ось як створити токен з розширенням 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
Для використання програми Memo на Solana у нас є два можливі шляхи.
Створення власної "сирої" інструкції memo таким чином:
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
}),
Or we can use the Memo program SDK after downloading the package like this:
npm i @solana/spl-memo
In our example we're going to use the second option and it will look like this:
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
If we don't want to enforce the transfer to come with a memo, we can do so using the memo_transfer_disable
function and it would look like so:
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(())
}