solana_account_decoder/
parse_token_extension.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
pub use solana_account_decoder_client_types::token::{
    UiConfidentialTransferAccount, UiConfidentialTransferFeeAmount,
    UiConfidentialTransferFeeConfig, UiConfidentialTransferMint, UiCpiGuard, UiDefaultAccountState,
    UiExtension, UiGroupMemberPointer, UiGroupPointer, UiInterestBearingConfig, UiMemoTransfer,
    UiMetadataPointer, UiMintCloseAuthority, UiPermanentDelegate, UiTokenGroup, UiTokenGroupMember,
    UiTokenMetadata, UiTransferFee, UiTransferFeeAmount, UiTransferFeeConfig, UiTransferHook,
    UiTransferHookAccount,
};
use {
    crate::parse_token::convert_account_state,
    solana_sdk::{clock::UnixTimestamp, program_pack::Pack},
    spl_token_2022::{
        extension::{self, BaseState, BaseStateWithExtensions, ExtensionType, StateWithExtensions},
        solana_program::pubkey::Pubkey,
        solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey,
    },
    spl_token_group_interface::state::{TokenGroup, TokenGroupMember},
    spl_token_metadata_interface::state::TokenMetadata,
};

pub fn parse_extension<S: BaseState + Pack>(
    extension_type: &ExtensionType,
    account: &StateWithExtensions<S>,
) -> UiExtension {
    match extension_type {
        ExtensionType::Uninitialized => UiExtension::Uninitialized,
        ExtensionType::TransferFeeConfig => account
            .get_extension::<extension::transfer_fee::TransferFeeConfig>()
            .map(|&extension| {
                UiExtension::TransferFeeConfig(convert_transfer_fee_config(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::TransferFeeAmount => account
            .get_extension::<extension::transfer_fee::TransferFeeAmount>()
            .map(|&extension| {
                UiExtension::TransferFeeAmount(convert_transfer_fee_amount(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::MintCloseAuthority => account
            .get_extension::<extension::mint_close_authority::MintCloseAuthority>()
            .map(|&extension| {
                UiExtension::MintCloseAuthority(convert_mint_close_authority(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::ConfidentialTransferMint => account
            .get_extension::<extension::confidential_transfer::ConfidentialTransferMint>()
            .map(|&extension| {
                UiExtension::ConfidentialTransferMint(convert_confidential_transfer_mint(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::ConfidentialTransferFeeConfig => account
            .get_extension::<extension::confidential_transfer_fee::ConfidentialTransferFeeConfig>()
            .map(|&extension| {
                UiExtension::ConfidentialTransferFeeConfig(
                    convert_confidential_transfer_fee_config(extension),
                )
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::ConfidentialTransferAccount => account
            .get_extension::<extension::confidential_transfer::ConfidentialTransferAccount>()
            .map(|&extension| {
                UiExtension::ConfidentialTransferAccount(convert_confidential_transfer_account(
                    extension,
                ))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::ConfidentialTransferFeeAmount => account
            .get_extension::<extension::confidential_transfer_fee::ConfidentialTransferFeeAmount>()
            .map(|&extension| {
                UiExtension::ConfidentialTransferFeeAmount(
                    convert_confidential_transfer_fee_amount(extension),
                )
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::DefaultAccountState => account
            .get_extension::<extension::default_account_state::DefaultAccountState>()
            .map(|&extension| {
                UiExtension::DefaultAccountState(convert_default_account_state(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::ImmutableOwner => UiExtension::ImmutableOwner,
        ExtensionType::MemoTransfer => account
            .get_extension::<extension::memo_transfer::MemoTransfer>()
            .map(|&extension| UiExtension::MemoTransfer(convert_memo_transfer(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::NonTransferable => UiExtension::NonTransferable,
        ExtensionType::InterestBearingConfig => account
            .get_extension::<extension::interest_bearing_mint::InterestBearingConfig>()
            .map(|&extension| {
                UiExtension::InterestBearingConfig(convert_interest_bearing_config(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::CpiGuard => account
            .get_extension::<extension::cpi_guard::CpiGuard>()
            .map(|&extension| UiExtension::CpiGuard(convert_cpi_guard(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::PermanentDelegate => account
            .get_extension::<extension::permanent_delegate::PermanentDelegate>()
            .map(|&extension| UiExtension::PermanentDelegate(convert_permanent_delegate(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::NonTransferableAccount => UiExtension::NonTransferableAccount,
        ExtensionType::MetadataPointer => account
            .get_extension::<extension::metadata_pointer::MetadataPointer>()
            .map(|&extension| UiExtension::MetadataPointer(convert_metadata_pointer(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::TokenMetadata => account
            .get_variable_len_extension::<TokenMetadata>()
            .map(|extension| UiExtension::TokenMetadata(convert_token_metadata(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::TransferHook => account
            .get_extension::<extension::transfer_hook::TransferHook>()
            .map(|&extension| UiExtension::TransferHook(convert_transfer_hook(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::TransferHookAccount => account
            .get_extension::<extension::transfer_hook::TransferHookAccount>()
            .map(|&extension| {
                UiExtension::TransferHookAccount(convert_transfer_hook_account(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::GroupPointer => account
            .get_extension::<extension::group_pointer::GroupPointer>()
            .map(|&extension| UiExtension::GroupPointer(convert_group_pointer(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::GroupMemberPointer => account
            .get_extension::<extension::group_member_pointer::GroupMemberPointer>()
            .map(|&extension| {
                UiExtension::GroupMemberPointer(convert_group_member_pointer(extension))
            })
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::TokenGroup => account
            .get_extension::<TokenGroup>()
            .map(|&extension| UiExtension::TokenGroup(convert_token_group(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
        ExtensionType::TokenGroupMember => account
            .get_extension::<TokenGroupMember>()
            .map(|&extension| UiExtension::TokenGroupMember(convert_token_group_member(extension)))
            .unwrap_or(UiExtension::UnparseableExtension),
    }
}

fn convert_transfer_fee(transfer_fee: extension::transfer_fee::TransferFee) -> UiTransferFee {
    UiTransferFee {
        epoch: u64::from(transfer_fee.epoch),
        maximum_fee: u64::from(transfer_fee.maximum_fee),
        transfer_fee_basis_points: u16::from(transfer_fee.transfer_fee_basis_points),
    }
}

fn convert_transfer_fee_config(
    transfer_fee_config: extension::transfer_fee::TransferFeeConfig,
) -> UiTransferFeeConfig {
    let transfer_fee_config_authority: Option<Pubkey> =
        transfer_fee_config.transfer_fee_config_authority.into();
    let withdraw_withheld_authority: Option<Pubkey> =
        transfer_fee_config.withdraw_withheld_authority.into();

    UiTransferFeeConfig {
        transfer_fee_config_authority: transfer_fee_config_authority
            .map(|pubkey| pubkey.to_string()),
        withdraw_withheld_authority: withdraw_withheld_authority.map(|pubkey| pubkey.to_string()),
        withheld_amount: u64::from(transfer_fee_config.withheld_amount),
        older_transfer_fee: convert_transfer_fee(transfer_fee_config.older_transfer_fee),
        newer_transfer_fee: convert_transfer_fee(transfer_fee_config.newer_transfer_fee),
    }
}

fn convert_transfer_fee_amount(
    transfer_fee_amount: extension::transfer_fee::TransferFeeAmount,
) -> UiTransferFeeAmount {
    UiTransferFeeAmount {
        withheld_amount: u64::from(transfer_fee_amount.withheld_amount),
    }
}

fn convert_mint_close_authority(
    mint_close_authority: extension::mint_close_authority::MintCloseAuthority,
) -> UiMintCloseAuthority {
    let authority: Option<Pubkey> = mint_close_authority.close_authority.into();
    UiMintCloseAuthority {
        close_authority: authority.map(|pubkey| pubkey.to_string()),
    }
}

fn convert_default_account_state(
    default_account_state: extension::default_account_state::DefaultAccountState,
) -> UiDefaultAccountState {
    let account_state = spl_token_2022::state::AccountState::try_from(default_account_state.state)
        .unwrap_or_default();
    UiDefaultAccountState {
        account_state: convert_account_state(account_state),
    }
}

fn convert_memo_transfer(memo_transfer: extension::memo_transfer::MemoTransfer) -> UiMemoTransfer {
    UiMemoTransfer {
        require_incoming_transfer_memos: memo_transfer.require_incoming_transfer_memos.into(),
    }
}

fn convert_interest_bearing_config(
    interest_bearing_config: extension::interest_bearing_mint::InterestBearingConfig,
) -> UiInterestBearingConfig {
    let rate_authority: Option<Pubkey> = interest_bearing_config.rate_authority.into();

    UiInterestBearingConfig {
        rate_authority: rate_authority.map(|pubkey| pubkey.to_string()),
        initialization_timestamp: UnixTimestamp::from(
            interest_bearing_config.initialization_timestamp,
        ),
        pre_update_average_rate: i16::from(interest_bearing_config.pre_update_average_rate),
        last_update_timestamp: UnixTimestamp::from(interest_bearing_config.last_update_timestamp),
        current_rate: i16::from(interest_bearing_config.current_rate),
    }
}

fn convert_cpi_guard(cpi_guard: extension::cpi_guard::CpiGuard) -> UiCpiGuard {
    UiCpiGuard {
        lock_cpi: cpi_guard.lock_cpi.into(),
    }
}

fn convert_permanent_delegate(
    permanent_delegate: extension::permanent_delegate::PermanentDelegate,
) -> UiPermanentDelegate {
    let delegate: Option<Pubkey> = permanent_delegate.delegate.into();
    UiPermanentDelegate {
        delegate: delegate.map(|pubkey| pubkey.to_string()),
    }
}

pub fn convert_confidential_transfer_mint(
    confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint,
) -> UiConfidentialTransferMint {
    let authority: Option<Pubkey> = confidential_transfer_mint.authority.into();
    let auditor_elgamal_pubkey: Option<ElGamalPubkey> =
        confidential_transfer_mint.auditor_elgamal_pubkey.into();
    UiConfidentialTransferMint {
        authority: authority.map(|pubkey| pubkey.to_string()),
        auto_approve_new_accounts: confidential_transfer_mint.auto_approve_new_accounts.into(),
        auditor_elgamal_pubkey: auditor_elgamal_pubkey.map(|pubkey| pubkey.to_string()),
    }
}

pub fn convert_confidential_transfer_fee_config(
    confidential_transfer_fee_config: extension::confidential_transfer_fee::ConfidentialTransferFeeConfig,
) -> UiConfidentialTransferFeeConfig {
    let authority: Option<Pubkey> = confidential_transfer_fee_config.authority.into();
    let withdraw_withheld_authority_elgamal_pubkey: Option<ElGamalPubkey> =
        confidential_transfer_fee_config
            .withdraw_withheld_authority_elgamal_pubkey
            .into();
    UiConfidentialTransferFeeConfig {
        authority: authority.map(|pubkey| pubkey.to_string()),
        withdraw_withheld_authority_elgamal_pubkey: withdraw_withheld_authority_elgamal_pubkey
            .map(|pubkey| pubkey.to_string()),
        harvest_to_mint_enabled: confidential_transfer_fee_config
            .harvest_to_mint_enabled
            .into(),
        withheld_amount: format!("{}", confidential_transfer_fee_config.withheld_amount),
    }
}

fn convert_confidential_transfer_account(
    confidential_transfer_account: extension::confidential_transfer::ConfidentialTransferAccount,
) -> UiConfidentialTransferAccount {
    UiConfidentialTransferAccount {
        approved: confidential_transfer_account.approved.into(),
        elgamal_pubkey: format!("{}", confidential_transfer_account.elgamal_pubkey),
        pending_balance_lo: format!("{}", confidential_transfer_account.pending_balance_lo),
        pending_balance_hi: format!("{}", confidential_transfer_account.pending_balance_hi),
        available_balance: format!("{}", confidential_transfer_account.available_balance),
        decryptable_available_balance: format!(
            "{}",
            confidential_transfer_account.decryptable_available_balance
        ),
        allow_confidential_credits: confidential_transfer_account
            .allow_confidential_credits
            .into(),
        allow_non_confidential_credits: confidential_transfer_account
            .allow_non_confidential_credits
            .into(),
        pending_balance_credit_counter: confidential_transfer_account
            .pending_balance_credit_counter
            .into(),
        maximum_pending_balance_credit_counter: confidential_transfer_account
            .maximum_pending_balance_credit_counter
            .into(),
        expected_pending_balance_credit_counter: confidential_transfer_account
            .expected_pending_balance_credit_counter
            .into(),
        actual_pending_balance_credit_counter: confidential_transfer_account
            .actual_pending_balance_credit_counter
            .into(),
    }
}

fn convert_confidential_transfer_fee_amount(
    confidential_transfer_fee_amount: extension::confidential_transfer_fee::ConfidentialTransferFeeAmount,
) -> UiConfidentialTransferFeeAmount {
    UiConfidentialTransferFeeAmount {
        withheld_amount: format!("{}", confidential_transfer_fee_amount.withheld_amount),
    }
}

fn convert_metadata_pointer(
    metadata_pointer: extension::metadata_pointer::MetadataPointer,
) -> UiMetadataPointer {
    let authority: Option<Pubkey> = metadata_pointer.authority.into();
    let metadata_address: Option<Pubkey> = metadata_pointer.metadata_address.into();
    UiMetadataPointer {
        authority: authority.map(|pubkey| pubkey.to_string()),
        metadata_address: metadata_address.map(|pubkey| pubkey.to_string()),
    }
}

fn convert_token_metadata(token_metadata: TokenMetadata) -> UiTokenMetadata {
    let update_authority: Option<Pubkey> = token_metadata.update_authority.into();
    UiTokenMetadata {
        update_authority: update_authority.map(|pubkey| pubkey.to_string()),
        mint: token_metadata.mint.to_string(),
        name: token_metadata.name,
        symbol: token_metadata.symbol,
        uri: token_metadata.uri,
        additional_metadata: token_metadata.additional_metadata,
    }
}

fn convert_transfer_hook(transfer_hook: extension::transfer_hook::TransferHook) -> UiTransferHook {
    let authority: Option<Pubkey> = transfer_hook.authority.into();
    let program_id: Option<Pubkey> = transfer_hook.program_id.into();
    UiTransferHook {
        authority: authority.map(|pubkey| pubkey.to_string()),
        program_id: program_id.map(|pubkey| pubkey.to_string()),
    }
}

fn convert_transfer_hook_account(
    transfer_hook: extension::transfer_hook::TransferHookAccount,
) -> UiTransferHookAccount {
    UiTransferHookAccount {
        transferring: transfer_hook.transferring.into(),
    }
}

fn convert_group_pointer(group_pointer: extension::group_pointer::GroupPointer) -> UiGroupPointer {
    let authority: Option<Pubkey> = group_pointer.authority.into();
    let group_address: Option<Pubkey> = group_pointer.group_address.into();
    UiGroupPointer {
        authority: authority.map(|pubkey| pubkey.to_string()),
        group_address: group_address.map(|pubkey| pubkey.to_string()),
    }
}

fn convert_group_member_pointer(
    member_pointer: extension::group_member_pointer::GroupMemberPointer,
) -> UiGroupMemberPointer {
    let authority: Option<Pubkey> = member_pointer.authority.into();
    let member_address: Option<Pubkey> = member_pointer.member_address.into();
    UiGroupMemberPointer {
        authority: authority.map(|pubkey| pubkey.to_string()),
        member_address: member_address.map(|pubkey| pubkey.to_string()),
    }
}

fn convert_token_group(token_group: TokenGroup) -> UiTokenGroup {
    let update_authority: Option<Pubkey> = token_group.update_authority.into();
    UiTokenGroup {
        update_authority: update_authority.map(|pubkey| pubkey.to_string()),
        mint: token_group.mint.to_string(),
        size: token_group.size.into(),
        max_size: token_group.max_size.into(),
    }
}

fn convert_token_group_member(member: TokenGroupMember) -> UiTokenGroupMember {
    UiTokenGroupMember {
        mint: member.mint.to_string(),
        group: member.group.to_string(),
        member_number: member.member_number.into(),
    }
}