L'Extension Memo Transfer
L'extension memo_transfer
(Transfert avec mémo) est une extension de compte de Token
qui impose que tous les transferts entrants d'un compte de token incluent un mémo, facilitant ainsi le suivi des transactions et l'identification des utilisateurs.
Initialisation du Compte de Jeton
Comme Anchor
ne possède aucune macro pour l'extension memo_transfer
nous allons créer un compte de Token
à l'aide de CPIs bruts.
Voici comment créer un compte de jeton avec l'extension 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>,
}
Transfert de Jetons avec un Mémo
Pour utiliser le programme Mémo sur Solana, nous avons deux options possibles.
Créer notre propre instruction mémo "brute" comme ceci :
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
});
Ou nous pouvons utiliser le SDK du programme Mémo après avoir téléchargé le package comme ceci :
npm i @solana/spl-memo
Dans notre exemple, nous allons utiliser la deuxième option, ce qui donnera le résultat suivant :
const memoInstruction = createMemoInstruction(
"Hello, world!",
[keypair.publicKey],
);
const transferInstruction = createTransferCheckedInstruction(
tokenAccount,
mint.publicKey,
destinationTokenAccount.publicKey,
keypair.publicKey,
BigInt(100e6),
6,
undefined,
TOKEN_2022_PROGRAM_ID,
);
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`);
Désactiver et Activer le Mémo
Si nous ne voulons pas imposer l'ajout d'un mémo lors du transfert, nous pouvons utiliser la fonction memo_transfer_disable
comme ceci :
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(())
}