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