1

making a SOL transaction in Anchor seems to be as easy as:

    let cpi_context = CpiContext::new(  ctx.accounts.system_program.to_account_info(),
        system_program::Transfer{
            from: ctx.accounts.payer.to_account_info(),
            to: ctx.accounts.receiver.to_account_info(),
        },
    );
    system_program::transfer(cpi_context, lamports)?;

but how could we add a Memo to that transaction in anchor?

2 Answers 2

1

@Henry E, the msg!() is is definitely not the same as using memo, the main benefit I see with memo is that it can provide quicker filtering when checking transactions, For instance:

let signatures = await provider.connection.getSignaturesForAddress(
      myPublicKey,
      { limit: 1000 },
      "confirmed"
    );
console.log("Signatures: ", signatures);

The log will show the following:

Signatures:  [
  {
    blockTime: 1663638143,
    confirmationStatus: 'confirmed',
    err: null,
    memo: null,
    signature: '5GbkFvY5Kq4auxQdQvUBzdsWHvvQbQmufzyX13WCrsMWKVQvDzXUjMG8SeRqV7rnAGgAJp2pf14EPZDmHJPfPeXz',
    slot: 8
  },
  {
    blockTime: 1663638142,
    confirmationStatus: 'confirmed',
    err: null,
    memo: null,
    signature: '4pwfuhu9ykReLHo86nxwFmi6gTsp6RSV4DmShwJu1VRC2V8S14TDd6YiyVufd7smHX21j7s72gp5KCW2PsgjuYgu',
    slot: 7
  }
]

as you see memo is null and the logs generated via msg! are not displayed at all here, so filtering doesn't seem possible.

I have tried invoking memo program within the function as you suggested but doesn't seem to work:

use crate::state::Receiver;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::invoke;
use crate::state::Memo;
use anchor_lang::system_program;
use spl_memo::build_memo;

#[derive(Accounts)]
pub struct TransferFunds<'info> {
    pub payer: Signer<'info>,
    #[account(mut)]
    pub receiver: Account<'info, Receiver>,
    pub system_program: Program<'info, System>,
    pub memo_program: Program<'info, Memo>,
}

pub fn transfer_funds_with_memo(ctx: Context<TransferFunds>, lamports: u64) -> Result<()> {
    let transfer_cpi_context = CpiContext::new(
        ctx.accounts.system_program.to_account_info(),
        system_program::Transfer {
            from: ctx.accounts.payer.to_account_info(),
            to: ctx.accounts.receiver.to_account_info(),
        },
    );
    system_program::transfer(transfer_cpi_context, lamports)?;
    let memo_ix = build_memo("WORKS!!".to_string().as_bytes(), &[]);
    invoke(&memo_ix, &[ctx.accounts.payer.to_account_info()])?;
    Ok(())
}

And I test the code with the following JS:

let tx = await program.methods
  .transferFundsWithMemo(100)
  .accounts({
    payer: provider.wallet.publicKey,
    receiver: receiverPubkey,
    memoProgram: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
  })
  .signers([])
  .rpc({ commitment: "confirmed" });

let signatures = await provider.connection.getSignaturesForAddress(
  receiverPubkey,
  params,
  "confirmed"
);
console.log("SIGNATURES: ", signatures);

But signature is still displayed without Memo, do you have any clue about what I might be missing?

# Response:
SIGNATURES:  [
  {
    blockTime: 1663646785,
    confirmationStatus: 'confirmed',
    err: null,
    memo: null,
    signature: '5hmwwtj7C8gNDg2edBWvyhuzFFFgifse5mJzZNFPuFV7Ma7W3BFoNYRJqcYk49mHQeH3k2n8vGu69nTFEUt7GKjV',
    slot: 9
  },

  ...
  ...
]

UPDATE Actually seems like some kind of bug, because If I fetch the transaction the memo is displayed, but not when listing signatures:

console.log("TX: ", await provider.connection.getTransaction("5hmwwtj7C8gNDg2edBWvyhuzFFFgifse5mJzZNFPuFV7Ma7W3BFoNYRJqcYk49mHQeH3k2n8vGu69nTFEUt7GKjV", { commitment: "confirmed" }));

# Response:
{
  blockTime: 1663650404,
  meta: {
    err: null,
    fee: 5000,
    innerInstructions: [ [Object] ],
    loadedAddresses: { readonly: [], writable: [] },
    logMessages: [
      'Program 9fkuQsynjfrPda7aWQjyYi4wDm6btb84fu1PwuqewiF invoke [1]',
      'Program log: Instruction: PlaceSolBet',
      'Program 11111111111111111111111111111111 invoke [2]',
      'Program 11111111111111111111111111111111 success',
      'Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [2]',
      'Program log: Memo (len 7): "WORKS!!"',
      'Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr consumed 3953 of 192080 compute units',
      'Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success',

      ....
      ....

5
  • msg!() shows up in the program logs. You can get the signatures for an address and then look up the transactions linked to those signatures to get the program logs. It's an extra step sure but it's the one most commonly used by people logging data. Alternatively you can use the built in event system in anchor, which also logs data to the program log in bytecode format and needs to be deserialized using the IDL.
    – Henry E
    Sep 20 at 7:44
  • 1
    I don't know.. feels like a lot of overhead specially if we are talking about thousands of transactions. Don't you agree that getSignaturesForAddress() should retrieve the attached memo instead of memo: null which is actually wrong and will confuse developers (already happened to me)
    – Ander
    Sep 20 at 9:06
  • I've heard of very few people using the memo program which might explain why the support for it is so vague and lacking. Program logs are currently the way to go
    – Henry E
    Sep 20 at 10:08
  • The memo field is populated if you add an instruction to the memo program in your transaction on the client side, and not if you preform a CPI call to it from another program.
    – Jon C
    Sep 21 at 23:04
  • yes I figured that out already, but is there anything we can do about it in rust to fix it? was it decided to work that way for some particular reason? because clients can taint those instructions. Although if clients can do it.. maybe doesn't make much sense doing it on Rust, because will not be trusted data anyways. Thanks @Jon C.
    – Ander
    Sep 22 at 5:02
0

The answer is that you can't. The CPI executes as if it were a single instruction. You would have to CPI to the memo program separately in the same program function that calls transfer.

3
  • Thanks @Henry E, but could you provide an example on how to apply CPI to memo program? I don’t know which functions need to be exported and from which library.
    – Ander
    Sep 19 at 15:07
  • 1
    I haven't seen any examples in the wild of people calling the memo program via CPI. It's honestly simpler just to use the msg!() macro. But if you really need to use the spl_memo program it is available in a rust crate. docs.rs/spl-memo/3.0.1/spl_memo . You import this and call build_memo() to create the instruction. Check the memo docs spl.solana.com/memo
    – Henry E
    Sep 19 at 15:13
  • Jaysus I didn’t even know that was a valid alternative ! Just used it for debugging. Thanks again, you were really helpful.
    – Ander
    Sep 19 at 15:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.