solana_account_decoder_wasm/
parse_token_extension.rs1pub 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}