solana_account_decoder_wasm/
parse_token_extension.rs

1pub use solana_account_decoder_client_types_wasm::token::UiConfidentialMintBurn;
2pub use solana_account_decoder_client_types_wasm::token::UiConfidentialTransferAccount;
3pub use solana_account_decoder_client_types_wasm::token::UiConfidentialTransferFeeAmount;
4pub use solana_account_decoder_client_types_wasm::token::UiConfidentialTransferFeeConfig;
5pub use solana_account_decoder_client_types_wasm::token::UiConfidentialTransferMint;
6pub use solana_account_decoder_client_types_wasm::token::UiCpiGuard;
7pub use solana_account_decoder_client_types_wasm::token::UiDefaultAccountState;
8pub use solana_account_decoder_client_types_wasm::token::UiExtension;
9pub use solana_account_decoder_client_types_wasm::token::UiGroupMemberPointer;
10pub use solana_account_decoder_client_types_wasm::token::UiGroupPointer;
11pub use solana_account_decoder_client_types_wasm::token::UiInterestBearingConfig;
12pub use solana_account_decoder_client_types_wasm::token::UiMemoTransfer;
13pub use solana_account_decoder_client_types_wasm::token::UiMetadataPointer;
14pub use solana_account_decoder_client_types_wasm::token::UiMintCloseAuthority;
15pub use solana_account_decoder_client_types_wasm::token::UiPausableConfig;
16pub use solana_account_decoder_client_types_wasm::token::UiPermanentDelegate;
17pub use solana_account_decoder_client_types_wasm::token::UiScaledUiAmountConfig;
18pub use solana_account_decoder_client_types_wasm::token::UiTokenGroup;
19pub use solana_account_decoder_client_types_wasm::token::UiTokenGroupMember;
20pub use solana_account_decoder_client_types_wasm::token::UiTokenMetadata;
21pub use solana_account_decoder_client_types_wasm::token::UiTransferFee;
22pub use solana_account_decoder_client_types_wasm::token::UiTransferFeeAmount;
23pub use solana_account_decoder_client_types_wasm::token::UiTransferFeeConfig;
24pub use solana_account_decoder_client_types_wasm::token::UiTransferHook;
25pub use solana_account_decoder_client_types_wasm::token::UiTransferHookAccount;
26use solana_clock::UnixTimestamp;
27use solana_program_pack::Pack;
28use solana_pubkey::Pubkey;
29use spl_token_2022::extension::BaseState;
30use spl_token_2022::extension::BaseStateWithExtensions;
31use spl_token_2022::extension::ExtensionType;
32use spl_token_2022::extension::StateWithExtensions;
33use spl_token_2022::extension::{self};
34use spl_token_2022::solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey;
35use spl_token_group_interface::state::TokenGroup;
36use spl_token_group_interface::state::TokenGroupMember;
37use spl_token_metadata_interface::state::TokenMetadata;
38
39use crate::parse_token::convert_account_state;
40
41pub fn parse_extension<S: BaseState + Pack>(
42	extension_type: &ExtensionType,
43	account: &StateWithExtensions<S>,
44) -> UiExtension {
45	match extension_type {
46		ExtensionType::Uninitialized => UiExtension::Uninitialized,
47		ExtensionType::TransferFeeConfig => {
48			account
49				.get_extension::<extension::transfer_fee::TransferFeeConfig>()
50				.map(|&extension| {
51					UiExtension::TransferFeeConfig(convert_transfer_fee_config(extension))
52				})
53				.unwrap_or(UiExtension::UnparseableExtension)
54		}
55		ExtensionType::TransferFeeAmount => {
56			account
57				.get_extension::<extension::transfer_fee::TransferFeeAmount>()
58				.map(|&extension| {
59					UiExtension::TransferFeeAmount(convert_transfer_fee_amount(extension))
60				})
61				.unwrap_or(UiExtension::UnparseableExtension)
62		}
63		ExtensionType::MintCloseAuthority => {
64			account
65				.get_extension::<extension::mint_close_authority::MintCloseAuthority>()
66				.map(|&extension| {
67					UiExtension::MintCloseAuthority(convert_mint_close_authority(extension))
68				})
69				.unwrap_or(UiExtension::UnparseableExtension)
70		}
71		ExtensionType::ConfidentialTransferMint => {
72			account
73				.get_extension::<extension::confidential_transfer::ConfidentialTransferMint>()
74				.map(|&extension| {
75					UiExtension::ConfidentialTransferMint(convert_confidential_transfer_mint(
76						extension,
77					))
78				})
79				.unwrap_or(UiExtension::UnparseableExtension)
80		}
81		ExtensionType::ConfidentialTransferFeeConfig => {
82			account
83				.get_extension::<extension::confidential_transfer_fee::ConfidentialTransferFeeConfig>(
84				)
85				.map(|&extension| {
86					UiExtension::ConfidentialTransferFeeConfig(
87						convert_confidential_transfer_fee_config(extension),
88					)
89				})
90				.unwrap_or(UiExtension::UnparseableExtension)
91		}
92		ExtensionType::ConfidentialTransferAccount => {
93			account
94				.get_extension::<extension::confidential_transfer::ConfidentialTransferAccount>()
95				.map(|&extension| {
96					UiExtension::ConfidentialTransferAccount(convert_confidential_transfer_account(
97						extension,
98					))
99				})
100				.unwrap_or(UiExtension::UnparseableExtension)
101		}
102		ExtensionType::ConfidentialTransferFeeAmount => {
103			account
104				.get_extension::<extension::confidential_transfer_fee::ConfidentialTransferFeeAmount>(
105				)
106				.map(|&extension| {
107					UiExtension::ConfidentialTransferFeeAmount(
108						convert_confidential_transfer_fee_amount(extension),
109					)
110				})
111				.unwrap_or(UiExtension::UnparseableExtension)
112		}
113		ExtensionType::DefaultAccountState => {
114			account
115				.get_extension::<extension::default_account_state::DefaultAccountState>()
116				.map(|&extension| {
117					UiExtension::DefaultAccountState(convert_default_account_state(extension))
118				})
119				.unwrap_or(UiExtension::UnparseableExtension)
120		}
121		ExtensionType::ImmutableOwner => UiExtension::ImmutableOwner,
122		ExtensionType::MemoTransfer => {
123			account
124				.get_extension::<extension::memo_transfer::MemoTransfer>()
125				.map(|&extension| UiExtension::MemoTransfer(convert_memo_transfer(extension)))
126				.unwrap_or(UiExtension::UnparseableExtension)
127		}
128		ExtensionType::NonTransferable => UiExtension::NonTransferable,
129		ExtensionType::InterestBearingConfig => {
130			account
131				.get_extension::<extension::interest_bearing_mint::InterestBearingConfig>()
132				.map(|&extension| {
133					UiExtension::InterestBearingConfig(convert_interest_bearing_config(extension))
134				})
135				.unwrap_or(UiExtension::UnparseableExtension)
136		}
137		ExtensionType::CpiGuard => {
138			account
139				.get_extension::<extension::cpi_guard::CpiGuard>()
140				.map(|&extension| UiExtension::CpiGuard(convert_cpi_guard(extension)))
141				.unwrap_or(UiExtension::UnparseableExtension)
142		}
143		ExtensionType::PermanentDelegate => {
144			account
145				.get_extension::<extension::permanent_delegate::PermanentDelegate>()
146				.map(|&extension| {
147					UiExtension::PermanentDelegate(convert_permanent_delegate(extension))
148				})
149				.unwrap_or(UiExtension::UnparseableExtension)
150		}
151		ExtensionType::NonTransferableAccount => UiExtension::NonTransferableAccount,
152		ExtensionType::MetadataPointer => {
153			account
154				.get_extension::<extension::metadata_pointer::MetadataPointer>()
155				.map(|&extension| UiExtension::MetadataPointer(convert_metadata_pointer(extension)))
156				.unwrap_or(UiExtension::UnparseableExtension)
157		}
158		ExtensionType::TokenMetadata => {
159			account
160				.get_variable_len_extension::<TokenMetadata>()
161				.map(|extension| UiExtension::TokenMetadata(convert_token_metadata(extension)))
162				.unwrap_or(UiExtension::UnparseableExtension)
163		}
164		ExtensionType::TransferHook => {
165			account
166				.get_extension::<extension::transfer_hook::TransferHook>()
167				.map(|&extension| UiExtension::TransferHook(convert_transfer_hook(extension)))
168				.unwrap_or(UiExtension::UnparseableExtension)
169		}
170		ExtensionType::TransferHookAccount => {
171			account
172				.get_extension::<extension::transfer_hook::TransferHookAccount>()
173				.map(|&extension| {
174					UiExtension::TransferHookAccount(convert_transfer_hook_account(extension))
175				})
176				.unwrap_or(UiExtension::UnparseableExtension)
177		}
178		ExtensionType::GroupPointer => {
179			account
180				.get_extension::<extension::group_pointer::GroupPointer>()
181				.map(|&extension| UiExtension::GroupPointer(convert_group_pointer(extension)))
182				.unwrap_or(UiExtension::UnparseableExtension)
183		}
184		ExtensionType::GroupMemberPointer => {
185			account
186				.get_extension::<extension::group_member_pointer::GroupMemberPointer>()
187				.map(|&extension| {
188					UiExtension::GroupMemberPointer(convert_group_member_pointer(extension))
189				})
190				.unwrap_or(UiExtension::UnparseableExtension)
191		}
192		ExtensionType::TokenGroup => {
193			account
194				.get_extension::<TokenGroup>()
195				.map(|&extension| UiExtension::TokenGroup(convert_token_group(extension)))
196				.unwrap_or(UiExtension::UnparseableExtension)
197		}
198		ExtensionType::TokenGroupMember => {
199			account
200				.get_extension::<TokenGroupMember>()
201				.map(|&extension| {
202					UiExtension::TokenGroupMember(convert_token_group_member(extension))
203				})
204				.unwrap_or(UiExtension::UnparseableExtension)
205		}
206		ExtensionType::ConfidentialMintBurn => {
207			account
208				.get_extension::<extension::confidential_mint_burn::ConfidentialMintBurn>()
209				.map(|&extension| {
210					UiExtension::ConfidentialMintBurn(convert_confidential_mint_burn(extension))
211				})
212				.unwrap_or(UiExtension::UnparseableExtension)
213		}
214		ExtensionType::ScaledUiAmount => {
215			account
216				.get_extension::<extension::scaled_ui_amount::ScaledUiAmountConfig>()
217				.map(|&extension| {
218					UiExtension::ScaledUiAmountConfig(convert_scaled_ui_amount(extension))
219				})
220				.unwrap_or(UiExtension::UnparseableExtension)
221		}
222		ExtensionType::Pausable => {
223			account
224				.get_extension::<extension::pausable::PausableConfig>()
225				.map(|&extension| UiExtension::PausableConfig(convert_pausable_config(extension)))
226				.unwrap_or(UiExtension::UnparseableExtension)
227		}
228		ExtensionType::PausableAccount => UiExtension::PausableAccount,
229	}
230}
231
232fn convert_transfer_fee(transfer_fee: extension::transfer_fee::TransferFee) -> UiTransferFee {
233	UiTransferFee {
234		epoch: u64::from(transfer_fee.epoch),
235		maximum_fee: u64::from(transfer_fee.maximum_fee),
236		transfer_fee_basis_points: u16::from(transfer_fee.transfer_fee_basis_points),
237	}
238}
239
240fn convert_transfer_fee_config(
241	transfer_fee_config: extension::transfer_fee::TransferFeeConfig,
242) -> UiTransferFeeConfig {
243	let transfer_fee_config_authority: Option<Pubkey> =
244		transfer_fee_config.transfer_fee_config_authority.into();
245	let withdraw_withheld_authority: Option<Pubkey> =
246		transfer_fee_config.withdraw_withheld_authority.into();
247
248	UiTransferFeeConfig {
249		transfer_fee_config_authority: transfer_fee_config_authority
250			.map(|pubkey| pubkey.to_string()),
251		withdraw_withheld_authority: withdraw_withheld_authority.map(|pubkey| pubkey.to_string()),
252		withheld_amount: u64::from(transfer_fee_config.withheld_amount),
253		older_transfer_fee: convert_transfer_fee(transfer_fee_config.older_transfer_fee),
254		newer_transfer_fee: convert_transfer_fee(transfer_fee_config.newer_transfer_fee),
255	}
256}
257
258fn convert_transfer_fee_amount(
259	transfer_fee_amount: extension::transfer_fee::TransferFeeAmount,
260) -> UiTransferFeeAmount {
261	UiTransferFeeAmount {
262		withheld_amount: u64::from(transfer_fee_amount.withheld_amount),
263	}
264}
265
266fn convert_mint_close_authority(
267	mint_close_authority: extension::mint_close_authority::MintCloseAuthority,
268) -> UiMintCloseAuthority {
269	let authority: Option<Pubkey> = mint_close_authority.close_authority.into();
270	UiMintCloseAuthority {
271		close_authority: authority.map(|pubkey| pubkey.to_string()),
272	}
273}
274
275fn convert_default_account_state(
276	default_account_state: extension::default_account_state::DefaultAccountState,
277) -> UiDefaultAccountState {
278	let account_state = spl_token_2022::state::AccountState::try_from(default_account_state.state)
279		.unwrap_or_default();
280	UiDefaultAccountState {
281		account_state: convert_account_state(account_state),
282	}
283}
284
285fn convert_memo_transfer(memo_transfer: extension::memo_transfer::MemoTransfer) -> UiMemoTransfer {
286	UiMemoTransfer {
287		require_incoming_transfer_memos: memo_transfer.require_incoming_transfer_memos.into(),
288	}
289}
290
291fn convert_interest_bearing_config(
292	interest_bearing_config: extension::interest_bearing_mint::InterestBearingConfig,
293) -> UiInterestBearingConfig {
294	let rate_authority: Option<Pubkey> = interest_bearing_config.rate_authority.into();
295
296	UiInterestBearingConfig {
297		rate_authority: rate_authority.map(|pubkey| pubkey.to_string()),
298		initialization_timestamp: UnixTimestamp::from(
299			interest_bearing_config.initialization_timestamp,
300		),
301		pre_update_average_rate: i16::from(interest_bearing_config.pre_update_average_rate),
302		last_update_timestamp: UnixTimestamp::from(interest_bearing_config.last_update_timestamp),
303		current_rate: i16::from(interest_bearing_config.current_rate),
304	}
305}
306
307fn convert_cpi_guard(cpi_guard: extension::cpi_guard::CpiGuard) -> UiCpiGuard {
308	UiCpiGuard {
309		lock_cpi: cpi_guard.lock_cpi.into(),
310	}
311}
312
313fn convert_permanent_delegate(
314	permanent_delegate: extension::permanent_delegate::PermanentDelegate,
315) -> UiPermanentDelegate {
316	let delegate: Option<Pubkey> = permanent_delegate.delegate.into();
317	UiPermanentDelegate {
318		delegate: delegate.map(|pubkey| pubkey.to_string()),
319	}
320}
321
322pub fn convert_confidential_transfer_mint(
323	confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint,
324) -> UiConfidentialTransferMint {
325	let authority: Option<Pubkey> = confidential_transfer_mint.authority.into();
326	let auditor_elgamal_pubkey: Option<PodElGamalPubkey> =
327		confidential_transfer_mint.auditor_elgamal_pubkey.into();
328	UiConfidentialTransferMint {
329		authority: authority.map(|pubkey| pubkey.to_string()),
330		auto_approve_new_accounts: confidential_transfer_mint.auto_approve_new_accounts.into(),
331		auditor_elgamal_pubkey: auditor_elgamal_pubkey.map(|pubkey| pubkey.to_string()),
332	}
333}
334
335pub fn convert_confidential_transfer_fee_config(
336	confidential_transfer_fee_config: extension::confidential_transfer_fee::ConfidentialTransferFeeConfig,
337) -> UiConfidentialTransferFeeConfig {
338	let authority: Option<Pubkey> = confidential_transfer_fee_config.authority.into();
339	let withdraw_withheld_authority_elgamal_pubkey: Option<PodElGamalPubkey> =
340		confidential_transfer_fee_config
341			.withdraw_withheld_authority_elgamal_pubkey
342			.into();
343	UiConfidentialTransferFeeConfig {
344		authority: authority.map(|pubkey| pubkey.to_string()),
345		withdraw_withheld_authority_elgamal_pubkey: withdraw_withheld_authority_elgamal_pubkey
346			.map(|pubkey| pubkey.to_string()),
347		harvest_to_mint_enabled: confidential_transfer_fee_config
348			.harvest_to_mint_enabled
349			.into(),
350		withheld_amount: format!("{}", confidential_transfer_fee_config.withheld_amount),
351	}
352}
353
354fn convert_confidential_transfer_account(
355	confidential_transfer_account: extension::confidential_transfer::ConfidentialTransferAccount,
356) -> UiConfidentialTransferAccount {
357	UiConfidentialTransferAccount {
358		approved: confidential_transfer_account.approved.into(),
359		elgamal_pubkey: format!("{}", confidential_transfer_account.elgamal_pubkey),
360		pending_balance_lo: format!("{}", confidential_transfer_account.pending_balance_lo),
361		pending_balance_hi: format!("{}", confidential_transfer_account.pending_balance_hi),
362		available_balance: format!("{}", confidential_transfer_account.available_balance),
363		decryptable_available_balance: format!(
364			"{}",
365			confidential_transfer_account.decryptable_available_balance
366		),
367		allow_confidential_credits: confidential_transfer_account
368			.allow_confidential_credits
369			.into(),
370		allow_non_confidential_credits: confidential_transfer_account
371			.allow_non_confidential_credits
372			.into(),
373		pending_balance_credit_counter: confidential_transfer_account
374			.pending_balance_credit_counter
375			.into(),
376		maximum_pending_balance_credit_counter: confidential_transfer_account
377			.maximum_pending_balance_credit_counter
378			.into(),
379		expected_pending_balance_credit_counter: confidential_transfer_account
380			.expected_pending_balance_credit_counter
381			.into(),
382		actual_pending_balance_credit_counter: confidential_transfer_account
383			.actual_pending_balance_credit_counter
384			.into(),
385	}
386}
387
388fn convert_confidential_transfer_fee_amount(
389	confidential_transfer_fee_amount: extension::confidential_transfer_fee::ConfidentialTransferFeeAmount,
390) -> UiConfidentialTransferFeeAmount {
391	UiConfidentialTransferFeeAmount {
392		withheld_amount: format!("{}", confidential_transfer_fee_amount.withheld_amount),
393	}
394}
395
396fn convert_metadata_pointer(
397	metadata_pointer: extension::metadata_pointer::MetadataPointer,
398) -> UiMetadataPointer {
399	let authority: Option<Pubkey> = metadata_pointer.authority.into();
400	let metadata_address: Option<Pubkey> = metadata_pointer.metadata_address.into();
401	UiMetadataPointer {
402		authority: authority.map(|pubkey| pubkey.to_string()),
403		metadata_address: metadata_address.map(|pubkey| pubkey.to_string()),
404	}
405}
406
407fn convert_token_metadata(token_metadata: TokenMetadata) -> UiTokenMetadata {
408	let update_authority: Option<Pubkey> = token_metadata.update_authority.into();
409	UiTokenMetadata {
410		update_authority: update_authority.map(|pubkey| pubkey.to_string()),
411		mint: token_metadata.mint.to_string(),
412		name: token_metadata.name,
413		symbol: token_metadata.symbol,
414		uri: token_metadata.uri,
415		additional_metadata: token_metadata.additional_metadata,
416	}
417}
418
419fn convert_transfer_hook(transfer_hook: extension::transfer_hook::TransferHook) -> UiTransferHook {
420	let authority: Option<Pubkey> = transfer_hook.authority.into();
421	let program_id: Option<Pubkey> = transfer_hook.program_id.into();
422	UiTransferHook {
423		authority: authority.map(|pubkey| pubkey.to_string()),
424		program_id: program_id.map(|pubkey| pubkey.to_string()),
425	}
426}
427
428fn convert_transfer_hook_account(
429	transfer_hook: extension::transfer_hook::TransferHookAccount,
430) -> UiTransferHookAccount {
431	UiTransferHookAccount {
432		transferring: transfer_hook.transferring.into(),
433	}
434}
435
436fn convert_group_pointer(group_pointer: extension::group_pointer::GroupPointer) -> UiGroupPointer {
437	let authority: Option<Pubkey> = group_pointer.authority.into();
438	let group_address: Option<Pubkey> = group_pointer.group_address.into();
439	UiGroupPointer {
440		authority: authority.map(|pubkey| pubkey.to_string()),
441		group_address: group_address.map(|pubkey| pubkey.to_string()),
442	}
443}
444
445fn convert_group_member_pointer(
446	member_pointer: extension::group_member_pointer::GroupMemberPointer,
447) -> UiGroupMemberPointer {
448	let authority: Option<Pubkey> = member_pointer.authority.into();
449	let member_address: Option<Pubkey> = member_pointer.member_address.into();
450	UiGroupMemberPointer {
451		authority: authority.map(|pubkey| pubkey.to_string()),
452		member_address: member_address.map(|pubkey| pubkey.to_string()),
453	}
454}
455
456fn convert_token_group(token_group: TokenGroup) -> UiTokenGroup {
457	let update_authority: Option<Pubkey> = token_group.update_authority.into();
458	UiTokenGroup {
459		update_authority: update_authority.map(|pubkey| pubkey.to_string()),
460		mint: token_group.mint.to_string(),
461		size: token_group.size.into(),
462		max_size: token_group.max_size.into(),
463	}
464}
465
466fn convert_token_group_member(member: TokenGroupMember) -> UiTokenGroupMember {
467	UiTokenGroupMember {
468		mint: member.mint.to_string(),
469		group: member.group.to_string(),
470		member_number: member.member_number.into(),
471	}
472}
473
474fn convert_confidential_mint_burn(
475	confidential_mint_burn: extension::confidential_mint_burn::ConfidentialMintBurn,
476) -> UiConfidentialMintBurn {
477	UiConfidentialMintBurn {
478		confidential_supply: confidential_mint_burn.confidential_supply.to_string(),
479		decryptable_supply: confidential_mint_burn.decryptable_supply.to_string(),
480		supply_elgamal_pubkey: confidential_mint_burn.supply_elgamal_pubkey.to_string(),
481		pending_burn: confidential_mint_burn.pending_burn.to_string(),
482	}
483}
484
485fn convert_scaled_ui_amount(
486	scaled_ui_amount_config: extension::scaled_ui_amount::ScaledUiAmountConfig,
487) -> UiScaledUiAmountConfig {
488	let authority: Option<Pubkey> = scaled_ui_amount_config.authority.into();
489	let multiplier: f64 = scaled_ui_amount_config.multiplier.into();
490	let new_multiplier_effective_timestamp: i64 = scaled_ui_amount_config
491		.new_multiplier_effective_timestamp
492		.into();
493	let new_multiplier: f64 = scaled_ui_amount_config.new_multiplier.into();
494	UiScaledUiAmountConfig {
495		authority: authority.map(|pubkey| pubkey.to_string()),
496		multiplier: multiplier.to_string(),
497		new_multiplier_effective_timestamp,
498		new_multiplier: new_multiplier.to_string(),
499	}
500}
501
502fn convert_pausable_config(
503	pausable_config: extension::pausable::PausableConfig,
504) -> UiPausableConfig {
505	let authority: Option<Pubkey> = pausable_config.authority.into();
506	UiPausableConfig {
507		authority: authority.map(|pubkey| pubkey.to_string()),
508		paused: pausable_config.paused.into(),
509	}
510}