solana_account_decoder/
parse_address_lookup_table.rs1use {
2 crate::parse_account_data::{ParsableAccount, ParseAccountError},
3 serde::{Deserialize, Serialize},
4 solana_address_lookup_table_interface::state::AddressLookupTable,
5 solana_instruction::error::InstructionError,
6};
7
8pub fn parse_address_lookup_table(
9 data: &[u8],
10) -> Result<LookupTableAccountType, ParseAccountError> {
11 AddressLookupTable::deserialize(data)
12 .map(|address_lookup_table| {
13 LookupTableAccountType::LookupTable(address_lookup_table.into())
14 })
15 .or_else(|err| match err {
16 InstructionError::UninitializedAccount => Ok(LookupTableAccountType::Uninitialized),
17 _ => Err(ParseAccountError::AccountNotParsable(
18 ParsableAccount::AddressLookupTable,
19 )),
20 })
21}
22
23#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
24#[serde(rename_all = "camelCase", tag = "type", content = "info")]
25pub enum LookupTableAccountType {
26 Uninitialized,
27 LookupTable(UiLookupTable),
28}
29
30#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
31#[serde(rename_all = "camelCase")]
32pub struct UiLookupTable {
33 pub deactivation_slot: String,
34 pub last_extended_slot: String,
35 pub last_extended_slot_start_index: u8,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub authority: Option<String>,
38 pub addresses: Vec<String>,
39}
40
41impl From<AddressLookupTable<'_>> for UiLookupTable {
42 fn from(address_lookup_table: AddressLookupTable) -> Self {
43 Self {
44 deactivation_slot: address_lookup_table.meta.deactivation_slot.to_string(),
45 last_extended_slot: address_lookup_table.meta.last_extended_slot.to_string(),
46 last_extended_slot_start_index: address_lookup_table
47 .meta
48 .last_extended_slot_start_index,
49 authority: address_lookup_table
50 .meta
51 .authority
52 .map(|authority| authority.to_string()),
53 addresses: address_lookup_table
54 .addresses
55 .iter()
56 .map(|address| address.to_string())
57 .collect(),
58 }
59 }
60}
61
62#[cfg(test)]
63mod test {
64 use {
65 super::*,
66 solana_address_lookup_table_interface::state::{LookupTableMeta, LOOKUP_TABLE_META_SIZE},
67 solana_pubkey::Pubkey,
68 std::borrow::Cow,
69 };
70
71 #[test]
72 fn test_parse_address_lookup_table() {
73 let authority = Pubkey::new_unique();
74 let deactivation_slot = 1;
75 let last_extended_slot = 2;
76 let last_extended_slot_start_index = 3;
77 let lookup_table_meta = LookupTableMeta {
78 deactivation_slot,
79 last_extended_slot,
80 last_extended_slot_start_index,
81 authority: Some(authority),
82 ..LookupTableMeta::default()
83 };
84 let num_addresses = 42;
85 let mut addresses = Vec::with_capacity(num_addresses);
86 addresses.resize_with(num_addresses, Pubkey::new_unique);
87 let lookup_table = AddressLookupTable {
88 meta: lookup_table_meta,
89 addresses: Cow::Owned(addresses),
90 };
91 let lookup_table_data = AddressLookupTable::serialize_for_tests(lookup_table).unwrap();
92
93 let parsing_result = parse_address_lookup_table(&lookup_table_data).unwrap();
94 if let LookupTableAccountType::LookupTable(ui_lookup_table) = parsing_result {
95 assert_eq!(
96 ui_lookup_table.deactivation_slot,
97 deactivation_slot.to_string()
98 );
99 assert_eq!(
100 ui_lookup_table.last_extended_slot,
101 last_extended_slot.to_string()
102 );
103 assert_eq!(
104 ui_lookup_table.last_extended_slot_start_index,
105 last_extended_slot_start_index
106 );
107 assert_eq!(ui_lookup_table.authority, Some(authority.to_string()));
108 assert_eq!(ui_lookup_table.addresses.len(), num_addresses);
109 }
110
111 assert_eq!(
112 parse_address_lookup_table(&[0u8; LOOKUP_TABLE_META_SIZE]).unwrap(),
113 LookupTableAccountType::Uninitialized
114 );
115 assert!(parse_address_lookup_table(&[]).is_err());
116 }
117}