solana_binary_encoder/
extract_memos.rs

1use {
2    crate::{
3        parse_instruction::parse_memo_data,
4        transaction_status::VersionedTransactionWithStatusMeta,
5        // instruction::CompiledInstruction,
6        // message::{AccountKeys, Message, SanitizedMessage},
7        // message::{Message},
8        // account_keys::AccountKeys,
9        // pubkey::Pubkey,
10    },
11    solana_sdk::{
12        instruction::CompiledInstruction,
13        message::{AccountKeys, Message, SanitizedMessage},
14        pubkey::Pubkey,
15    },
16    lazy_static::lazy_static,
17};
18
19// A helper function to convert spl_memo::v1::id() as spl_sdk::pubkey::Pubkey to
20// solana_sdk::pubkey::Pubkey
21pub fn spl_memo_id_v1() -> Pubkey {
22    *MEMO_PROGRAM_ID_V1
23}
24
25// A helper function to convert spl_memo::id() as spl_sdk::pubkey::Pubkey to
26// solana_sdk::pubkey::Pubkey
27pub 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
64// impl ExtractMemos for SanitizedMessage {
65//     fn extract_memos(&self) -> Vec<String> {
66//         extract_memos_inner(&self.account_keys(), self.instructions())
67//     }
68// }
69
70impl 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}