solana_account_decoder/
parse_address_lookup_table.rs

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