Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit f3f9995

Browse files
committed
token-cli: impl display for interest and transfer fee
1 parent bdd0d49 commit f3f9995

File tree

3 files changed

+85
-17
lines changed

3 files changed

+85
-17
lines changed

token/cli/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,7 @@ async fn command_address(
16261626

16271627
async fn command_display(config: &Config<'_>, address: Pubkey) -> CommandResult {
16281628
let account_data = config.get_account_checked(&address).await?;
1629+
let epoch_info = config.rpc_client.get_epoch_info().await?;
16291630

16301631
let decimals = if let Some(mint_address) = get_token_account_mint(&account_data.data) {
16311632
Some(config.get_mint_info(&mint_address, None).await?.decimals)
@@ -1648,6 +1649,7 @@ async fn command_display(config: &Config<'_>, address: Pubkey) -> CommandResult
16481649
let cli_output = CliTokenAccount {
16491650
address: address.to_string(),
16501651
program_id: config.program_id.to_string(),
1652+
epoch: epoch_info.epoch,
16511653
decimals,
16521654
is_associated: associated_address == address,
16531655
account,
@@ -1658,6 +1660,7 @@ async fn command_display(config: &Config<'_>, address: Pubkey) -> CommandResult
16581660
Ok(TokenAccountType::Mint(mint)) => {
16591661
let cli_output = CliMint {
16601662
address: address.to_string(),
1663+
epoch: epoch_info.epoch,
16611664
program_id: config.program_id.to_string(),
16621665
mint,
16631666
};

token/cli/src/output.rs

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::{config::Config, sort::UnsupportedAccount};
2-
use console::Emoji;
2+
use console::{style, Emoji};
33
use serde::{Deserialize, Serialize, Serializer};
44
use solana_account_decoder::{
55
parse_token::{UiAccountState, UiMint, UiMultisig, UiTokenAccount, UiTokenAmount},
66
parse_token_extension::{
7-
UiDefaultAccountState, UiExtension, UiMemoTransfer, UiMintCloseAuthority,
8-
UiTransferFeeAmount,
7+
UiDefaultAccountState, UiExtension, UiInterestBearingConfig, UiMemoTransfer,
8+
UiMintCloseAuthority, UiTransferFee, UiTransferFeeAmount, UiTransferFeeConfig,
99
},
1010
};
1111
use solana_cli_output::{display::writeln_name_value, OutputFormat, QuietDisplay, VerboseDisplay};
@@ -168,11 +168,11 @@ impl fmt::Display for CliMultisig {
168168
let n = self.multisig.num_valid_signers;
169169

170170
writeln!(f)?;
171-
writeln_name_value(f, "SPL Token Multisig", " ")?;
171+
writeln!(f, "{}", style("SPL Token Multisig").bold())?;
172172
writeln_name_value(f, " Address:", &self.address)?;
173173
writeln_name_value(f, " Program:", &self.program_id)?;
174174
writeln_name_value(f, " M/N:", &format!("{}/{}", m, n))?;
175-
writeln_name_value(f, " Signers:", " ")?;
175+
writeln!(f, " {}", style("Signers:").bold())?;
176176
let width = if n >= 9 { 4 } else { 3 };
177177
for i in 0..n as usize {
178178
let title = format!(" {1:>0$}:", width, i + 1);
@@ -188,6 +188,7 @@ impl fmt::Display for CliMultisig {
188188
pub(crate) struct CliTokenAccount {
189189
pub(crate) address: String,
190190
pub(crate) program_id: String,
191+
pub(crate) epoch: u64,
191192
pub(crate) decimals: Option<u8>,
192193
pub(crate) is_associated: bool,
193194
#[serde(flatten)]
@@ -200,7 +201,7 @@ impl VerboseDisplay for CliTokenAccount {}
200201
impl fmt::Display for CliTokenAccount {
201202
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202203
writeln!(f)?;
203-
writeln_name_value(f, "SPL Token Account", " ")?;
204+
writeln!(f, "{}", style("SPL Token Account").bold())?;
204205
if self.is_associated {
205206
writeln_name_value(f, " Address:", &self.address)?;
206207
} else {
@@ -235,7 +236,7 @@ impl fmt::Display for CliTokenAccount {
235236
writeln_name_value(f, " Owner:", &self.account.owner)?;
236237
writeln_name_value(f, " State:", &format!("{:?}", self.account.state))?;
237238
if let Some(delegate) = &self.account.delegate {
238-
writeln_name_value(f, " Delegation:", " ")?;
239+
writeln!(f, " {}", style("Delegation:").bold())?;
239240
writeln_name_value(f, " Delegate:", delegate)?;
240241
let allowance = self.account.delegated_amount.as_ref().unwrap();
241242
writeln_name_value(f, " Allowance:", &allowance.real_number_string_trimmed())?;
@@ -252,9 +253,9 @@ impl fmt::Display for CliTokenAccount {
252253
)?;
253254

254255
if !self.account.extensions.is_empty() {
255-
writeln_name_value(f, "Extensions", " ")?;
256+
writeln!(f, "{}", style("Extensions:").bold())?;
256257
for extension in &self.account.extensions {
257-
display_ui_extension(f, extension)?;
258+
display_ui_extension(f, self.epoch, extension)?;
258259
}
259260
}
260261

@@ -272,6 +273,7 @@ impl fmt::Display for CliTokenAccount {
272273
pub(crate) struct CliMint {
273274
pub(crate) address: String,
274275
pub(crate) program_id: String,
276+
pub(crate) epoch: u64,
275277
#[serde(flatten)]
276278
pub(crate) mint: UiMint,
277279
}
@@ -282,7 +284,7 @@ impl VerboseDisplay for CliMint {}
282284
impl fmt::Display for CliMint {
283285
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284286
writeln!(f)?;
285-
writeln_name_value(f, "SPL Token Mint", " ")?;
287+
writeln!(f, "{}", style("SPL Token Mint").bold())?;
286288

287289
writeln_name_value(f, " Address:", &self.address)?;
288290
writeln_name_value(f, " Program:", &self.program_id)?;
@@ -303,9 +305,9 @@ impl fmt::Display for CliMint {
303305
)?;
304306

305307
if !self.mint.extensions.is_empty() {
306-
writeln_name_value(f, "Extensions", " ")?;
308+
writeln!(f, "{}", style("Extensions").bold())?;
307309
for extension in &self.mint.extensions {
308-
display_ui_extension(f, extension)?;
310+
display_ui_extension(f, self.epoch, extension)?;
309311
}
310312
}
311313

@@ -479,9 +481,54 @@ impl fmt::Display for CliTokenAccounts {
479481
}
480482
}
481483

482-
fn display_ui_extension(f: &mut fmt::Formatter, ui_extension: &UiExtension) -> fmt::Result {
484+
fn display_ui_extension(
485+
f: &mut fmt::Formatter,
486+
epoch: u64,
487+
ui_extension: &UiExtension,
488+
) -> fmt::Result {
483489
match ui_extension {
484-
UiExtension::TransferFeeConfig(_) => unimplemented!(), // annoying
490+
UiExtension::TransferFeeConfig(UiTransferFeeConfig {
491+
transfer_fee_config_authority,
492+
withdraw_withheld_authority,
493+
withheld_amount,
494+
older_transfer_fee,
495+
newer_transfer_fee,
496+
..
497+
}) => {
498+
let UiTransferFee {
499+
transfer_fee_basis_points,
500+
maximum_fee,
501+
..
502+
} = if newer_transfer_fee.epoch >= epoch {
503+
newer_transfer_fee
504+
} else {
505+
older_transfer_fee
506+
};
507+
508+
writeln!(f, " {}", style("Transfer fees:").bold())?;
509+
writeln!(
510+
f,
511+
" {} {}bps",
512+
style("Fee:").bold(),
513+
transfer_fee_basis_points
514+
)?;
515+
writeln_name_value(f, " Maximum fee:", &maximum_fee.to_string())?;
516+
writeln_name_value(
517+
f,
518+
" Config authority:",
519+
transfer_fee_config_authority
520+
.as_ref()
521+
.unwrap_or(&String::new()),
522+
)?;
523+
writeln_name_value(
524+
f,
525+
" Withdrawal authority:",
526+
withdraw_withheld_authority
527+
.as_ref()
528+
.unwrap_or(&String::new()),
529+
)?;
530+
writeln_name_value(f, " Withheld fees:", &withheld_amount.to_string())
531+
}
485532
UiExtension::TransferFeeAmount(UiTransferFeeAmount { withheld_amount }) => {
486533
writeln_name_value(f, " Transfer fees withheld:", &withheld_amount.to_string())
487534
}
@@ -497,7 +544,7 @@ fn display_ui_extension(f: &mut fmt::Formatter, ui_extension: &UiExtension) -> f
497544
UiExtension::DefaultAccountState(UiDefaultAccountState { account_state }) => {
498545
writeln_name_value(f, " Default state:", &format!("{:?}", account_state))
499546
}
500-
UiExtension::ImmutableOwner => writeln_name_value(f, " Immutable owner", " "),
547+
UiExtension::ImmutableOwner => writeln!(f, " {}", style("Immutable owner").bold()),
501548
UiExtension::MemoTransfer(UiMemoTransfer {
502549
require_incoming_transfer_memos,
503550
}) => writeln_name_value(
@@ -509,8 +556,25 @@ fn display_ui_extension(f: &mut fmt::Formatter, ui_extension: &UiExtension) -> f
509556
"Not required"
510557
},
511558
),
512-
UiExtension::NonTransferable => writeln_name_value(f, " Non-transferable", " "),
513-
UiExtension::InterestBearingConfig(_) => unimplemented!(), // little annoying
559+
UiExtension::NonTransferable => writeln!(f, " {}", style("Non-transferable").bold()),
560+
UiExtension::InterestBearingConfig(UiInterestBearingConfig {
561+
rate_authority,
562+
current_rate,
563+
..
564+
}) => {
565+
writeln!(f, " {}", style("Interest-bearing:").bold())?;
566+
writeln!(
567+
f,
568+
" {} {}bps",
569+
style("Interest rate:").bold(),
570+
current_rate
571+
)?;
572+
writeln_name_value(
573+
f,
574+
" Rate authority:",
575+
rate_authority.as_ref().unwrap_or(&String::new()),
576+
)
577+
}
514578
UiExtension::UnparseableExtension => panic!("err here"),
515579
UiExtension::Uninitialized => panic!("err here...?"),
516580
}

token/cli/src/sort.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub(crate) fn sort_and_parse_token_accounts(
5656
let parsed_account = CliTokenAccount {
5757
address,
5858
program_id: program_id.to_string(),
59+
epoch: 0,
5960
decimals: None,
6061
account: ui_token_account,
6162
is_associated,

0 commit comments

Comments
 (0)