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 spl_token_2022::{
14 extension::{self, BaseState, BaseStateWithExtensions, ExtensionType, StateWithExtensions},
15 solana_program::pubkey::Pubkey,
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 = spl_token_2022::state::AccountState::try_from(default_account_state.state)
206 .unwrap_or_default();
207 UiDefaultAccountState {
208 account_state: convert_account_state(account_state),
209 }
210}
211
212fn convert_memo_transfer(memo_transfer: extension::memo_transfer::MemoTransfer) -> UiMemoTransfer {
213 UiMemoTransfer {
214 require_incoming_transfer_memos: memo_transfer.require_incoming_transfer_memos.into(),
215 }
216}
217
218fn convert_interest_bearing_config(
219 interest_bearing_config: extension::interest_bearing_mint::InterestBearingConfig,
220) -> UiInterestBearingConfig {
221 let rate_authority: Option<Pubkey> = interest_bearing_config.rate_authority.into();
222
223 UiInterestBearingConfig {
224 rate_authority: rate_authority.map(|pubkey| pubkey.to_string()),
225 initialization_timestamp: UnixTimestamp::from(
226 interest_bearing_config.initialization_timestamp,
227 ),
228 pre_update_average_rate: i16::from(interest_bearing_config.pre_update_average_rate),
229 last_update_timestamp: UnixTimestamp::from(interest_bearing_config.last_update_timestamp),
230 current_rate: i16::from(interest_bearing_config.current_rate),
231 }
232}
233
234fn convert_cpi_guard(cpi_guard: extension::cpi_guard::CpiGuard) -> UiCpiGuard {
235 UiCpiGuard {
236 lock_cpi: cpi_guard.lock_cpi.into(),
237 }
238}
239
240fn convert_permanent_delegate(
241 permanent_delegate: extension::permanent_delegate::PermanentDelegate,
242) -> UiPermanentDelegate {
243 let delegate: Option<Pubkey> = permanent_delegate.delegate.into();
244 UiPermanentDelegate {
245 delegate: delegate.map(|pubkey| pubkey.to_string()),
246 }
247}
248
249pub fn convert_confidential_transfer_mint(
250 confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint,
251) -> UiConfidentialTransferMint {
252 let authority: Option<Pubkey> = confidential_transfer_mint.authority.into();
253 let auditor_elgamal_pubkey: Option<PodElGamalPubkey> =
254 confidential_transfer_mint.auditor_elgamal_pubkey.into();
255 UiConfidentialTransferMint {
256 authority: authority.map(|pubkey| pubkey.to_string()),
257 auto_approve_new_accounts: confidential_transfer_mint.auto_approve_new_accounts.into(),
258 auditor_elgamal_pubkey: auditor_elgamal_pubkey.map(|pubkey| pubkey.to_string()),
259 }
260}
261
262pub fn convert_confidential_transfer_fee_config(
263 confidential_transfer_fee_config: extension::confidential_transfer_fee::ConfidentialTransferFeeConfig,
264) -> UiConfidentialTransferFeeConfig {
265 let authority: Option<Pubkey> = confidential_transfer_fee_config.authority.into();
266 let withdraw_withheld_authority_elgamal_pubkey: Option<PodElGamalPubkey> =
267 confidential_transfer_fee_config
268 .withdraw_withheld_authority_elgamal_pubkey
269 .into();
270 UiConfidentialTransferFeeConfig {
271 authority: authority.map(|pubkey| pubkey.to_string()),
272 withdraw_withheld_authority_elgamal_pubkey: withdraw_withheld_authority_elgamal_pubkey
273 .map(|pubkey| pubkey.to_string()),
274 harvest_to_mint_enabled: confidential_transfer_fee_config
275 .harvest_to_mint_enabled
276 .into(),
277 withheld_amount: format!("{}", confidential_transfer_fee_config.withheld_amount),
278 }
279}
280
281fn convert_confidential_transfer_account(
282 confidential_transfer_account: extension::confidential_transfer::ConfidentialTransferAccount,
283) -> UiConfidentialTransferAccount {
284 UiConfidentialTransferAccount {
285 approved: confidential_transfer_account.approved.into(),
286 elgamal_pubkey: format!("{}", confidential_transfer_account.elgamal_pubkey),
287 pending_balance_lo: format!("{}", confidential_transfer_account.pending_balance_lo),
288 pending_balance_hi: format!("{}", confidential_transfer_account.pending_balance_hi),
289 available_balance: format!("{}", confidential_transfer_account.available_balance),
290 decryptable_available_balance: format!(
291 "{}",
292 confidential_transfer_account.decryptable_available_balance
293 ),
294 allow_confidential_credits: confidential_transfer_account
295 .allow_confidential_credits
296 .into(),
297 allow_non_confidential_credits: confidential_transfer_account
298 .allow_non_confidential_credits
299 .into(),
300 pending_balance_credit_counter: confidential_transfer_account
301 .pending_balance_credit_counter
302 .into(),
303 maximum_pending_balance_credit_counter: confidential_transfer_account
304 .maximum_pending_balance_credit_counter
305 .into(),
306 expected_pending_balance_credit_counter: confidential_transfer_account
307 .expected_pending_balance_credit_counter
308 .into(),
309 actual_pending_balance_credit_counter: confidential_transfer_account
310 .actual_pending_balance_credit_counter
311 .into(),
312 }
313}
314
315fn convert_confidential_transfer_fee_amount(
316 confidential_transfer_fee_amount: extension::confidential_transfer_fee::ConfidentialTransferFeeAmount,
317) -> UiConfidentialTransferFeeAmount {
318 UiConfidentialTransferFeeAmount {
319 withheld_amount: format!("{}", confidential_transfer_fee_amount.withheld_amount),
320 }
321}
322
323fn convert_metadata_pointer(
324 metadata_pointer: extension::metadata_pointer::MetadataPointer,
325) -> UiMetadataPointer {
326 let authority: Option<Pubkey> = metadata_pointer.authority.into();
327 let metadata_address: Option<Pubkey> = metadata_pointer.metadata_address.into();
328 UiMetadataPointer {
329 authority: authority.map(|pubkey| pubkey.to_string()),
330 metadata_address: metadata_address.map(|pubkey| pubkey.to_string()),
331 }
332}
333
334fn convert_token_metadata(token_metadata: TokenMetadata) -> UiTokenMetadata {
335 let update_authority: Option<Pubkey> = token_metadata.update_authority.into();
336 UiTokenMetadata {
337 update_authority: update_authority.map(|pubkey| pubkey.to_string()),
338 mint: token_metadata.mint.to_string(),
339 name: token_metadata.name,
340 symbol: token_metadata.symbol,
341 uri: token_metadata.uri,
342 additional_metadata: token_metadata.additional_metadata,
343 }
344}
345
346fn convert_transfer_hook(transfer_hook: extension::transfer_hook::TransferHook) -> UiTransferHook {
347 let authority: Option<Pubkey> = transfer_hook.authority.into();
348 let program_id: Option<Pubkey> = transfer_hook.program_id.into();
349 UiTransferHook {
350 authority: authority.map(|pubkey| pubkey.to_string()),
351 program_id: program_id.map(|pubkey| pubkey.to_string()),
352 }
353}
354
355fn convert_transfer_hook_account(
356 transfer_hook: extension::transfer_hook::TransferHookAccount,
357) -> UiTransferHookAccount {
358 UiTransferHookAccount {
359 transferring: transfer_hook.transferring.into(),
360 }
361}
362
363fn convert_group_pointer(group_pointer: extension::group_pointer::GroupPointer) -> UiGroupPointer {
364 let authority: Option<Pubkey> = group_pointer.authority.into();
365 let group_address: Option<Pubkey> = group_pointer.group_address.into();
366 UiGroupPointer {
367 authority: authority.map(|pubkey| pubkey.to_string()),
368 group_address: group_address.map(|pubkey| pubkey.to_string()),
369 }
370}
371
372fn convert_group_member_pointer(
373 member_pointer: extension::group_member_pointer::GroupMemberPointer,
374) -> UiGroupMemberPointer {
375 let authority: Option<Pubkey> = member_pointer.authority.into();
376 let member_address: Option<Pubkey> = member_pointer.member_address.into();
377 UiGroupMemberPointer {
378 authority: authority.map(|pubkey| pubkey.to_string()),
379 member_address: member_address.map(|pubkey| pubkey.to_string()),
380 }
381}
382
383fn convert_token_group(token_group: TokenGroup) -> UiTokenGroup {
384 let update_authority: Option<Pubkey> = token_group.update_authority.into();
385 UiTokenGroup {
386 update_authority: update_authority.map(|pubkey| pubkey.to_string()),
387 mint: token_group.mint.to_string(),
388 size: token_group.size.into(),
389 max_size: token_group.max_size.into(),
390 }
391}
392
393fn convert_token_group_member(member: TokenGroupMember) -> UiTokenGroupMember {
394 UiTokenGroupMember {
395 mint: member.mint.to_string(),
396 group: member.group.to_string(),
397 member_number: member.member_number.into(),
398 }
399}
400
401fn convert_confidential_mint_burn(
402 confidential_mint_burn: extension::confidential_mint_burn::ConfidentialMintBurn,
403) -> UiConfidentialMintBurn {
404 UiConfidentialMintBurn {
405 confidential_supply: confidential_mint_burn.confidential_supply.to_string(),
406 decryptable_supply: confidential_mint_burn.decryptable_supply.to_string(),
407 supply_elgamal_pubkey: confidential_mint_burn.supply_elgamal_pubkey.to_string(),
408 }
409}
410
411fn convert_scaled_ui_amount(
412 scaled_ui_amount_config: extension::scaled_ui_amount::ScaledUiAmountConfig,
413) -> UiScaledUiAmountConfig {
414 let authority: Option<Pubkey> = scaled_ui_amount_config.authority.into();
415 let multiplier: f64 = scaled_ui_amount_config.multiplier.into();
416 let new_multiplier_effective_timestamp: i64 = scaled_ui_amount_config
417 .new_multiplier_effective_timestamp
418 .into();
419 let new_multiplier: f64 = scaled_ui_amount_config.new_multiplier.into();
420 UiScaledUiAmountConfig {
421 authority: authority.map(|pubkey| pubkey.to_string()),
422 multiplier: multiplier.to_string(),
423 new_multiplier_effective_timestamp,
424 new_multiplier: new_multiplier.to_string(),
425 }
426}
427
428fn convert_pausable_config(
429 pausable_config: extension::pausable::PausableConfig,
430) -> UiPausableConfig {
431 let authority: Option<Pubkey> = pausable_config.authority.into();
432 UiPausableConfig {
433 authority: authority.map(|pubkey| pubkey.to_string()),
434 paused: pausable_config.paused.into(),
435 }
436}