Anchor
使用Anchor的Token2022

使用Anchor的Token2022

Memo 傳輸擴展

MemoTranfer 擴展是一個 Token 賬戶擴展,強制要求所有進入的代幣賬戶轉賬都包含備忘錄,從而促進更高效的交易追蹤和用戶識別。

初始化代幣賬戶

由於 Anchor 沒有任何針對 memo_transfer 擴展的宏,我們將使用原始 CPI 創建一個 Token 賬戶。

以下是如何使用 Memo 傳輸擴展創建代幣的方法:

rust
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>,
}

帶備忘錄的代幣轉賬

要在 Solana 上使用 Memo 程式,我們有兩條可行的路徑。

構建我們自己的「原始」備忘錄指令,如下所示:

ts
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
}),

或者我們可以下載套件後使用 Memo 程式 SDK,如下所示:

text
npm i @solana/spl-memo

創建備忘錄後,下一個指令將是一個簡單的轉賬指令。

在我們的例子中,我們將使用第二個選項,結果如下:

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`);

禁用和啟用備忘錄

如果我們不想強制要求轉賬附帶備忘錄,可以使用 memo_transfer_disable 函數來實現,效果如下:

rust
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(())
}
Blueshift © 2025Commit: e573eab