solana_account_decoder_wasm/
parse_address_lookup_table.rs

1use 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}