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_interface::extension::BaseState;
30use spl_token_2022_interface::extension::BaseStateWithExtensions;
31use spl_token_2022_interface::extension::ExtensionType;
32use spl_token_2022_interface::extension::StateWithExtensions;
33use spl_token_2022_interface::extension::{self};
34use spl_token_2022_interface::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 =
279 spl_token_2022_interface::state::AccountState::try_from(default_account_state.state)
280 .unwrap_or_default();
281 UiDefaultAccountState {
282 account_state: convert_account_state(account_state),
283 }
284}
285
286fn convert_memo_transfer(memo_transfer: extension::memo_transfer::MemoTransfer) -> UiMemoTransfer {
287 UiMemoTransfer {
288 require_incoming_transfer_memos: memo_transfer.require_incoming_transfer_memos.into(),
289 }
290}
291
292fn convert_interest_bearing_config(
293 interest_bearing_config: extension::interest_bearing_mint::InterestBearingConfig,
294) -> UiInterestBearingConfig {
295 let rate_authority: Option<Pubkey> = interest_bearing_config.rate_authority.into();
296
297 UiInterestBearingConfig {
298 rate_authority: rate_authority.map(|pubkey| pubkey.to_string()),
299 initialization_timestamp: UnixTimestamp::from(
300 interest_bearing_config.initialization_timestamp,
301 ),
302 pre_update_average_rate: i16::from(interest_bearing_config.pre_update_average_rate),
303 last_update_timestamp: UnixTimestamp::from(interest_bearing_config.last_update_timestamp),
304 current_rate: i16::from(interest_bearing_config.current_rate),
305 }
306}
307
308fn convert_cpi_guard(cpi_guard: extension::cpi_guard::CpiGuard) -> UiCpiGuard {
309 UiCpiGuard {
310 lock_cpi: cpi_guard.lock_cpi.into(),
311 }
312}
313
314fn convert_permanent_delegate(
315 permanent_delegate: extension::permanent_delegate::PermanentDelegate,
316) -> UiPermanentDelegate {
317 let delegate: Option<Pubkey> = permanent_delegate.delegate.into();
318 UiPermanentDelegate {
319 delegate: delegate.map(|pubkey| pubkey.to_string()),
320 }
321}
322
323pub fn convert_confidential_transfer_mint(
324 confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint,
325) -> UiConfidentialTransferMint {
326 let authority: Option<Pubkey> = confidential_transfer_mint.authority.into();
327 let auditor_elgamal_pubkey: Option<PodElGamalPubkey> =
328 confidential_transfer_mint.auditor_elgamal_pubkey.into();
329 UiConfidentialTransferMint {
330 authority: authority.map(|pubkey| pubkey.to_string()),
331 auto_approve_new_accounts: confidential_transfer_mint.auto_approve_new_accounts.into(),
332 auditor_elgamal_pubkey: auditor_elgamal_pubkey.map(|pubkey| pubkey.to_string()),
333 }
334}
335
336pub fn convert_confidential_transfer_fee_config(
337 confidential_transfer_fee_config: extension::confidential_transfer_fee::ConfidentialTransferFeeConfig,
338) -> UiConfidentialTransferFeeConfig {
339 let authority: Option<Pubkey> = confidential_transfer_fee_config.authority.into();
340 let withdraw_withheld_authority_elgamal_pubkey: Option<PodElGamalPubkey> =
341 confidential_transfer_fee_config
342 .withdraw_withheld_authority_elgamal_pubkey
343 .into();
344 UiConfidentialTransferFeeConfig {
345 authority: authority.map(|pubkey| pubkey.to_string()),
346 withdraw_withheld_authority_elgamal_pubkey: withdraw_withheld_authority_elgamal_pubkey
347 .map(|pubkey| pubkey.to_string()),
348 harvest_to_mint_enabled: confidential_transfer_fee_config
349 .harvest_to_mint_enabled
350 .into(),
351 withheld_amount: format!("{}", confidential_transfer_fee_config.withheld_amount),
352 }
353}
354
355fn convert_confidential_transfer_account(
356 confidential_transfer_account: &extension::confidential_transfer::ConfidentialTransferAccount,
357) -> UiConfidentialTransferAccount {
358 UiConfidentialTransferAccount {
359 approved: confidential_transfer_account.approved.into(),
360 elgamal_pubkey: format!("{}", confidential_transfer_account.elgamal_pubkey),
361 pending_balance_lo: format!("{}", confidential_transfer_account.pending_balance_lo),
362 pending_balance_hi: format!("{}", confidential_transfer_account.pending_balance_hi),
363 available_balance: format!("{}", confidential_transfer_account.available_balance),
364 decryptable_available_balance: format!(
365 "{}",
366 confidential_transfer_account.decryptable_available_balance
367 ),
368 allow_confidential_credits: confidential_transfer_account
369 .allow_confidential_credits
370 .into(),
371 allow_non_confidential_credits: confidential_transfer_account
372 .allow_non_confidential_credits
373 .into(),
374 pending_balance_credit_counter: confidential_transfer_account
375 .pending_balance_credit_counter
376 .into(),
377 maximum_pending_balance_credit_counter: confidential_transfer_account
378 .maximum_pending_balance_credit_counter
379 .into(),
380 expected_pending_balance_credit_counter: confidential_transfer_account
381 .expected_pending_balance_credit_counter
382 .into(),
383 actual_pending_balance_credit_counter: confidential_transfer_account
384 .actual_pending_balance_credit_counter
385 .into(),
386 }
387}
388
389fn convert_confidential_transfer_fee_amount(
390 confidential_transfer_fee_amount: extension::confidential_transfer_fee::ConfidentialTransferFeeAmount,
391) -> UiConfidentialTransferFeeAmount {
392 UiConfidentialTransferFeeAmount {
393 withheld_amount: format!("{}", confidential_transfer_fee_amount.withheld_amount),
394 }
395}
396
397fn convert_metadata_pointer(
398 metadata_pointer: extension::metadata_pointer::MetadataPointer,
399) -> UiMetadataPointer {
400 let authority: Option<Pubkey> = metadata_pointer.authority.into();
401 let metadata_address: Option<Pubkey> = metadata_pointer.metadata_address.into();
402 UiMetadataPointer {
403 authority: authority.map(|pubkey| pubkey.to_string()),
404 metadata_address: metadata_address.map(|pubkey| pubkey.to_string()),
405 }
406}
407
408fn convert_token_metadata(token_metadata: TokenMetadata) -> UiTokenMetadata {
409 let update_authority: Option<Pubkey> = token_metadata.update_authority.into();
410 UiTokenMetadata {
411 update_authority: update_authority.map(|pubkey| pubkey.to_string()),
412 mint: token_metadata.mint.to_string(),
413 name: token_metadata.name,
414 symbol: token_metadata.symbol,
415 uri: token_metadata.uri,
416 additional_metadata: token_metadata.additional_metadata,
417 }
418}
419
420fn convert_transfer_hook(transfer_hook: extension::transfer_hook::TransferHook) -> UiTransferHook {
421 let authority: Option<Pubkey> = transfer_hook.authority.into();
422 let program_id: Option<Pubkey> = transfer_hook.program_id.into();
423 UiTransferHook {
424 authority: authority.map(|pubkey| pubkey.to_string()),
425 program_id: program_id.map(|pubkey| pubkey.to_string()),
426 }
427}
428
429fn convert_transfer_hook_account(
430 transfer_hook: extension::transfer_hook::TransferHookAccount,
431) -> UiTransferHookAccount {
432 UiTransferHookAccount {
433 transferring: transfer_hook.transferring.into(),
434 }
435}
436
437fn convert_group_pointer(group_pointer: extension::group_pointer::GroupPointer) -> UiGroupPointer {
438 let authority: Option<Pubkey> = group_pointer.authority.into();
439 let group_address: Option<Pubkey> = group_pointer.group_address.into();
440 UiGroupPointer {
441 authority: authority.map(|pubkey| pubkey.to_string()),
442 group_address: group_address.map(|pubkey| pubkey.to_string()),
443 }
444}
445
446fn convert_group_member_pointer(
447 member_pointer: extension::group_member_pointer::GroupMemberPointer,
448) -> UiGroupMemberPointer {
449 let authority: Option<Pubkey> = member_pointer.authority.into();
450 let member_address: Option<Pubkey> = member_pointer.member_address.into();
451 UiGroupMemberPointer {
452 authority: authority.map(|pubkey| pubkey.to_string()),
453 member_address: member_address.map(|pubkey| pubkey.to_string()),
454 }
455}
456
457fn convert_token_group(token_group: TokenGroup) -> UiTokenGroup {
458 let update_authority: Option<Pubkey> = token_group.update_authority.into();
459 UiTokenGroup {
460 update_authority: update_authority.map(|pubkey| pubkey.to_string()),
461 mint: token_group.mint.to_string(),
462 size: token_group.size.into(),
463 max_size: token_group.max_size.into(),
464 }
465}
466
467fn convert_token_group_member(member: TokenGroupMember) -> UiTokenGroupMember {
468 UiTokenGroupMember {
469 mint: member.mint.to_string(),
470 group: member.group.to_string(),
471 member_number: member.member_number.into(),
472 }
473}
474
475fn convert_confidential_mint_burn(
476 confidential_mint_burn: extension::confidential_mint_burn::ConfidentialMintBurn,
477) -> UiConfidentialMintBurn {
478 UiConfidentialMintBurn {
479 confidential_supply: confidential_mint_burn.confidential_supply.to_string(),
480 decryptable_supply: confidential_mint_burn.decryptable_supply.to_string(),
481 supply_elgamal_pubkey: confidential_mint_burn.supply_elgamal_pubkey.to_string(),
482 pending_burn: confidential_mint_burn.pending_burn.to_string(),
483 }
484}
485
486fn convert_scaled_ui_amount(
487 scaled_ui_amount_config: extension::scaled_ui_amount::ScaledUiAmountConfig,
488) -> UiScaledUiAmountConfig {
489 let authority: Option<Pubkey> = scaled_ui_amount_config.authority.into();
490 let multiplier: f64 = scaled_ui_amount_config.multiplier.into();
491 let new_multiplier_effective_timestamp: i64 = scaled_ui_amount_config
492 .new_multiplier_effective_timestamp
493 .into();
494 let new_multiplier: f64 = scaled_ui_amount_config.new_multiplier.into();
495 UiScaledUiAmountConfig {
496 authority: authority.map(|pubkey| pubkey.to_string()),
497 multiplier: multiplier.to_string(),
498 new_multiplier_effective_timestamp,
499 new_multiplier: new_multiplier.to_string(),
500 }
501}
502
503fn convert_pausable_config(
504 pausable_config: extension::pausable::PausableConfig,
505) -> UiPausableConfig {
506 let authority: Option<Pubkey> = pausable_config.authority.into();
507 UiPausableConfig {
508 authority: authority.map(|pubkey| pubkey.to_string()),
509 paused: pausable_config.paused.into(),
510 }
511}