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