solana_account_decoder/
parse_address_lookup_table.rs1use {
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}