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