solana_binary_encoder/
extract_memos.rs1use {
2 crate::{
3 parse_instruction::parse_memo_data,
4 transaction_status::VersionedTransactionWithStatusMeta,
5 },
11 solana_sdk::{
12 instruction::CompiledInstruction,
13 message::{AccountKeys, Message, SanitizedMessage},
14 pubkey::Pubkey,
15 },
16 lazy_static::lazy_static,
17};
18
19pub fn spl_memo_id_v1() -> Pubkey {
22 *MEMO_PROGRAM_ID_V1
23}
24
25pub fn spl_memo_id_v3() -> Pubkey {
28 *MEMO_PROGRAM_ID_V3
29}
30
31lazy_static! {
32 static ref MEMO_PROGRAM_ID_V1: Pubkey = Pubkey::new_from_array(spl_memo::v1::id().to_bytes());
33 static ref MEMO_PROGRAM_ID_V3: Pubkey = Pubkey::new_from_array(spl_memo::id().to_bytes());
34}
35
36pub fn extract_and_fmt_memos<T: ExtractMemos>(message: &T) -> Option<String> {
37 let memos = message.extract_memos();
38 if memos.is_empty() {
39 None
40 } else {
41 Some(memos.join("; "))
42 }
43}
44
45fn extract_and_fmt_memo_data(data: &[u8]) -> String {
46 let memo_len = data.len();
47 let parsed_memo = parse_memo_data(data).unwrap_or_else(|_| "(unparseable)".to_string());
48 format!("[{}] {}", memo_len, parsed_memo)
49}
50
51pub trait ExtractMemos {
52 fn extract_memos(&self) -> Vec<String>;
53}
54
55impl ExtractMemos for Message {
56 fn extract_memos(&self) -> Vec<String> {
57 extract_memos_inner(
58 &AccountKeys::new(&self.account_keys, None),
59 &self.instructions,
60 )
61 }
62}
63
64impl ExtractMemos for VersionedTransactionWithStatusMeta {
71 fn extract_memos(&self) -> Vec<String> {
72 extract_memos_inner(
73 &self.account_keys(),
74 self.transaction.message.instructions(),
75 )
76 }
77}
78
79enum KeyType<'a> {
80 MemoProgram,
81 OtherProgram,
82 Unknown(&'a Pubkey),
83}
84
85fn extract_memos_inner(
86 account_keys: &AccountKeys,
87 instructions: &[CompiledInstruction],
88) -> Vec<String> {
89 let mut account_keys: Vec<KeyType> = account_keys.iter().map(KeyType::Unknown).collect();
90 instructions
91 .iter()
92 .filter_map(|ix| {
93 let index = ix.program_id_index as usize;
94 let key_type = account_keys.get(index)?;
95 let memo_data = match key_type {
96 KeyType::MemoProgram => Some(&ix.data),
97 KeyType::OtherProgram => None,
98 KeyType::Unknown(program_id) => {
99 if **program_id == *MEMO_PROGRAM_ID_V1 || **program_id == *MEMO_PROGRAM_ID_V3 {
100 account_keys[index] = KeyType::MemoProgram;
101 Some(&ix.data)
102 } else {
103 account_keys[index] = KeyType::OtherProgram;
104 None
105 }
106 }
107 }?;
108 Some(extract_and_fmt_memo_data(memo_data))
109 })
110 .collect()
111}