anchor
anchor copied to clipboard
Memo program not working properly in Anchor transactions
Just realised that Memo program doesn't seem to work properly in Anchor transactions.
For instance a normal SOL transfer instruction + Memo:
const transaction = new Transaction();
transaction.add(
SystemProgram.transfer({
fromPubkey: my_keypair.publicKey,
toPubkey: receiverPubkey,
lamports: 50000,
})
);
transaction.add({
programId: MEMO_PROGRAM_ID,
keys: [],
data: Buffer.from("Hello world!", "utf8"),
});
let result = await provider.connection.sendTransaction(transaction, [my_keypair]);
await provider.connection.confirmTransaction(result, "confirmed");
console.log(await provider.connection.getSignaturesForAddress(receiverPubkey, { limit: 1000 }, "confirmed");
Will display the correct Memo "Hello world!" when calling getSignaturesForAddress():
[
{
blockTime: 1663653480,
confirmationStatus: 'confirmed',
err: null,
memo: '[12] Hello world!',
signature: '4sxYm9aex7sTB9HR4mJQuyEbAUnku2GMP8nfq8pepbszukTjseqFYVvpF8YrsobWC4JK4v3grhQievQymuaCWvb6',
slot: 10
},
{
blockTime: 1663653479,
confirmationStatus: 'confirmed',
err: null,
memo: null,
signature: '4frX4dHsnfRXGf8W9dz7MRqt6zTTaA5i3fuZJfXCF15CfGq4uxzpViUsULUojHN9y2f49mbxfuFLmocB2qxW4Z2h',
slot: 8
},
...
...
]
But the corresponding anchor program transaction doesn't display the memo when calling getSignaturesForAddress():
use crate::state::Receiver;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::invoke;
use anchor_lang::system_program;
use spl_memo::build_memo;
use solana_program::pubkey::Pubkey;
use spl_memo::ID;
#[derive(Debug, Clone)]
pub struct Memo;
impl anchor_lang::Id for Memo {
fn id() -> Pubkey {
ID
}
}
#[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("Hello world!".to_string().as_bytes(), &[]);
invoke(&memo_ix, &[ctx.accounts.payer.to_account_info()])?;
Ok(())
}
Testing the anchor program:
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);
Here memo is not displayed:
SIGNATURES: [
{
blockTime: 1663653479,
confirmationStatus: 'confirmed',
err: null,
memo: null, <------ NOTHING HERE!
signature: '4frX4dHsnfRXGf8W9dz7MRqt6zTTaA5i3fuZJfXCF15CfGq4uxzpViUsULUojHN9y2f49mbxfuFLmocB2qxW4Z2h',
slot: 8
},
...
...
]
Which is strange specially considering that the memo was successfully added and can be seen by fetching the transaction:
const tx = await provider.connection.getTransaction("4frX4dHsnfRXGf8W9dz7MRqt6zTTaA5i3fuZJfXCF15CfGq4uxzpViUsULUojHN9y2f49mbxfuFLmocB2qxW4Z2h", {commitment: "confirmed"});
console.log(tx);
Logs:
{
blockTime: 1663653479,
meta: {
err: null,
fee: 5000,
innerInstructions: [ [Object] ],
loadedAddresses: { readonly: [], writable: [] },
logMessages: [
'Program kul6QsynjfrPda7aWQjyYi4wDm6btb84fu1PwuqewiF invoke [1]',
'Program log: Instruction: PlaceSolBet',
'Program 11111111111111111111111111111111 invoke [2]',
'Program 11111111111111111111111111111111 success',
'Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [2]',
'Program log: Memo (len 12): "Hello world!"', <------ Here Memo is displayed!
'Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr consumed 3953 of 192080 compute units',
'Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success',
'Program kul6QsynjfrPda7aWQjyYi4wDm6btb84fu1PwuqewiF consumed 15111 of 200000 compute units',
'Program kul6QsynjfrPda7aWQjyYi4wDm6btb84fu1PwuqewiF success'
],
postBalances: [ 499999999975981900, 7850890, 1, 1, 1 ],
postTokenBalances: [],
preBalances: [ 499999999975986940, 7850880, 1, 1, 1 ],
preTokenBalances: [],
rewards: [],
status: { Ok: null }
},
slot: 8,
transaction: {
message: Message {
header: [Object],
accountKeys: [Array],
recentBlockhash: 'GFqrTKjo9ZAfLT6u4o7ydBU61AXSpGJpEKjvWYLgxBKX',
instructions: [Array],
indexToProgramIds: [Map]
},
signatures: [
'4frX4dHsnfRXGf8W9dz7MRqt6zTTaA5i3fuZJfXCF15CfGq4uxzpViUsULUojHN9y2f49mbxfuFLmocB2qxW4Z2h'
]
}
}
Great research! Being able to use getSignaturesForAddress() to get all transactions + memos at once is a huge performance boost vs having to query every single transaction individually. Hopefully they will fix this issue.
A lot of text here. Is the issue just that provider.connection.getSignaturesForAddress somehow drops the memo information passed back by the RPC call?
Happy to review a PR that fixes. It's also worth noting that connection.getSignaturesForAddress is a web3 library function, so the issue could lay there. 🤷