2021-08-30 20:14:18 -07:00
|
|
|
use {
|
2022-01-13 23:24:41 -08:00
|
|
|
crate::{parse_instruction::parse_memo_data, VersionedTransactionWithStatusMeta},
|
2021-09-02 23:28:52 -07:00
|
|
|
solana_sdk::{
|
2022-01-11 14:32:44 -08:00
|
|
|
instruction::CompiledInstruction,
|
2022-02-05 04:00:31 -08:00
|
|
|
message::{AccountKeys, Message, SanitizedMessage},
|
2021-09-02 23:28:52 -07:00
|
|
|
pubkey::Pubkey,
|
|
|
|
},
|
2021-08-30 20:14:18 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// A helper function to convert spl_memo::v1::id() as spl_sdk::pubkey::Pubkey to
|
|
|
|
// solana_sdk::pubkey::Pubkey
|
|
|
|
pub fn spl_memo_id_v1() -> Pubkey {
|
2022-01-11 14:32:44 -08:00
|
|
|
*MEMO_PROGRAM_ID_V1
|
2021-08-30 20:14:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// A helper function to convert spl_memo::id() as spl_sdk::pubkey::Pubkey to
|
|
|
|
// solana_sdk::pubkey::Pubkey
|
|
|
|
pub fn spl_memo_id_v3() -> Pubkey {
|
2022-01-11 14:32:44 -08:00
|
|
|
*MEMO_PROGRAM_ID_V3
|
|
|
|
}
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref MEMO_PROGRAM_ID_V1: Pubkey = Pubkey::new_from_array(spl_memo::v1::id().to_bytes());
|
|
|
|
static ref MEMO_PROGRAM_ID_V3: Pubkey = Pubkey::new_from_array(spl_memo::id().to_bytes());
|
2021-08-30 20:14:18 -07:00
|
|
|
}
|
|
|
|
|
2021-09-02 23:28:52 -07:00
|
|
|
pub fn extract_and_fmt_memos<T: ExtractMemos>(message: &T) -> Option<String> {
|
|
|
|
let memos = message.extract_memos();
|
2021-08-30 20:14:18 -07:00
|
|
|
if memos.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(memos.join("; "))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 14:32:44 -08:00
|
|
|
fn extract_and_fmt_memo_data(data: &[u8]) -> String {
|
|
|
|
let memo_len = data.len();
|
|
|
|
let parsed_memo = parse_memo_data(data).unwrap_or_else(|_| "(unparseable)".to_string());
|
|
|
|
format!("[{}] {}", memo_len, parsed_memo)
|
2021-09-02 23:28:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait ExtractMemos {
|
|
|
|
fn extract_memos(&self) -> Vec<String>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExtractMemos for Message {
|
|
|
|
fn extract_memos(&self) -> Vec<String> {
|
2022-02-05 04:00:31 -08:00
|
|
|
extract_memos_inner(
|
|
|
|
&AccountKeys::new(&self.account_keys, None),
|
|
|
|
&self.instructions,
|
|
|
|
)
|
2021-09-02 23:28:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExtractMemos for SanitizedMessage {
|
|
|
|
fn extract_memos(&self) -> Vec<String> {
|
2022-02-05 04:00:31 -08:00
|
|
|
extract_memos_inner(&self.account_keys(), self.instructions())
|
2021-09-02 23:28:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 23:24:41 -08:00
|
|
|
impl ExtractMemos for VersionedTransactionWithStatusMeta {
|
|
|
|
fn extract_memos(&self) -> Vec<String> {
|
|
|
|
extract_memos_inner(
|
2022-02-05 04:00:31 -08:00
|
|
|
&self.account_keys(),
|
2022-01-13 23:24:41 -08:00
|
|
|
self.transaction.message.instructions(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 14:32:44 -08:00
|
|
|
enum KeyType<'a> {
|
|
|
|
MemoProgram,
|
|
|
|
OtherProgram,
|
|
|
|
Unknown(&'a Pubkey),
|
|
|
|
}
|
|
|
|
|
2022-02-05 04:00:31 -08:00
|
|
|
fn extract_memos_inner(
|
|
|
|
account_keys: &AccountKeys,
|
2022-01-11 14:32:44 -08:00
|
|
|
instructions: &[CompiledInstruction],
|
|
|
|
) -> Vec<String> {
|
2022-02-05 04:00:31 -08:00
|
|
|
let mut account_keys: Vec<KeyType> = account_keys.iter().map(KeyType::Unknown).collect();
|
2022-01-11 14:32:44 -08:00
|
|
|
instructions
|
|
|
|
.iter()
|
|
|
|
.filter_map(|ix| {
|
|
|
|
let index = ix.program_id_index as usize;
|
|
|
|
let key_type = account_keys.get(index)?;
|
|
|
|
let memo_data = match key_type {
|
|
|
|
KeyType::MemoProgram => Some(&ix.data),
|
|
|
|
KeyType::OtherProgram => None,
|
|
|
|
KeyType::Unknown(program_id) => {
|
|
|
|
if **program_id == *MEMO_PROGRAM_ID_V1 || **program_id == *MEMO_PROGRAM_ID_V3 {
|
|
|
|
account_keys[index] = KeyType::MemoProgram;
|
|
|
|
Some(&ix.data)
|
|
|
|
} else {
|
|
|
|
account_keys[index] = KeyType::OtherProgram;
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}?;
|
|
|
|
Some(extract_and_fmt_memo_data(memo_data))
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2021-09-02 23:28:52 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2022-01-11 14:32:44 -08:00
|
|
|
use super::*;
|
2021-09-02 23:28:52 -07:00
|
|
|
|
|
|
|
#[test]
|
2022-01-11 14:32:44 -08:00
|
|
|
fn test_extract_memos_inner() {
|
2021-09-02 23:28:52 -07:00
|
|
|
let fee_payer = Pubkey::new_unique();
|
|
|
|
let another_program_id = Pubkey::new_unique();
|
|
|
|
let memo0 = "Test memo";
|
|
|
|
let memo1 = "🦖";
|
|
|
|
let expected_memos = vec![
|
|
|
|
format!("[{}] {}", memo0.len(), memo0),
|
|
|
|
format!("[{}] {}", memo1.len(), memo1),
|
|
|
|
];
|
|
|
|
let memo_instructions = vec![
|
|
|
|
CompiledInstruction {
|
|
|
|
program_id_index: 1,
|
|
|
|
accounts: vec![],
|
|
|
|
data: memo0.as_bytes().to_vec(),
|
|
|
|
},
|
|
|
|
CompiledInstruction {
|
|
|
|
program_id_index: 2,
|
|
|
|
accounts: vec![],
|
|
|
|
data: memo1.as_bytes().to_vec(),
|
|
|
|
},
|
|
|
|
CompiledInstruction {
|
|
|
|
program_id_index: 3,
|
|
|
|
accounts: vec![],
|
|
|
|
data: memo1.as_bytes().to_vec(),
|
|
|
|
},
|
|
|
|
];
|
2022-02-05 04:00:31 -08:00
|
|
|
let static_keys = vec![
|
2022-01-11 14:32:44 -08:00
|
|
|
fee_payer,
|
|
|
|
spl_memo_id_v1(),
|
|
|
|
another_program_id,
|
|
|
|
spl_memo_id_v3(),
|
|
|
|
];
|
2022-02-05 04:00:31 -08:00
|
|
|
let account_keys = AccountKeys::new(&static_keys, None);
|
2021-09-02 23:28:52 -07:00
|
|
|
|
2022-01-11 14:32:44 -08:00
|
|
|
assert_eq!(
|
2022-02-05 04:00:31 -08:00
|
|
|
extract_memos_inner(&account_keys, &memo_instructions),
|
2022-01-11 14:32:44 -08:00
|
|
|
expected_memos
|
|
|
|
);
|
2021-08-30 20:14:18 -07:00
|
|
|
}
|
|
|
|
}
|