trident-explorer 0.3.2

Trident explorer for Solana.
Documentation
use crate::{account::KeyedAccount, output::pretty_lamports_to_sol};
use console::style;
use serde::Serialize;
use solana_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey};
use std::fmt;

pub struct ProgramFieldVisibility {
    program_account: bool,
    programdata_account: bool,
}

impl ProgramFieldVisibility {
    pub fn new_all_enabled() -> Self {
        Self {
            program_account: true,
            programdata_account: true,
        }
    }

    pub fn new_all_disabled() -> Self {
        Self {
            program_account: false,
            programdata_account: false,
        }
    }

    pub fn program_account(&self) -> bool {
        self.program_account
    }

    pub fn enable_program_account(&mut self) -> &mut Self {
        self.program_account = true;
        self
    }

    pub fn disable_program_account(&mut self) -> &mut Self {
        self.program_account = false;
        self
    }

    pub fn programdata_account(&self) -> bool {
        self.programdata_account
    }

    pub fn enable_programdata_account(&mut self) -> &mut Self {
        self.programdata_account = true;
        self
    }

    pub fn disable_programdata_account(&mut self) -> &mut Self {
        self.programdata_account = false;
        self
    }
}

#[derive(Serialize)]
pub struct ProgramDataDeserialized {
    pub slot: u64,
    pub upgrade_authority_address: String,
    pub raw_program_data: String,
}

#[derive(Serialize)]
pub struct ProgramDeserialized {
    pub programdata_address: String,
}

#[derive(Serialize)]
pub struct DisplayProgramDataAccount {
    pub lamports: u64,
    pub data: ProgramDataDeserialized,
    pub owner: String,
    pub executable: bool,
    pub rent_epoch: u64,
}

#[derive(Serialize)]
pub struct DisplayProgramAccount {
    pub lamports: u64,
    pub data: ProgramDeserialized,
    pub owner: String,
    pub executable: bool,
    pub rent_epoch: u64,
}

#[derive(Serialize)]
pub struct DisplayUpgradeableProgram {
    pub program_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub program_account: Option<DisplayProgramAccount>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub programdata_account: Option<DisplayProgramDataAccount>,
}

impl DisplayUpgradeableProgram {
    pub fn from(
        program_account: &KeyedAccount,
        programdata_account: &KeyedAccount,
        slot: u64,
        upgrade_authority_address: &Option<Pubkey>,
        visibility: &ProgramFieldVisibility,
    ) -> Self {
        Self {
            program_id: program_account.pubkey.to_string(),
            program_account: if visibility.program_account {
                Some(DisplayProgramAccount {
                    lamports: program_account.account.lamports,
                    data: ProgramDeserialized {
                        programdata_address: programdata_account.pubkey.to_string(),
                    },
                    owner: program_account.account.owner.to_string(),
                    executable: program_account.account.executable,
                    rent_epoch: program_account.account.rent_epoch,
                })
            } else {
                None
            },
            programdata_account: if visibility.programdata_account {
                Some(DisplayProgramDataAccount {
                    lamports: programdata_account.account.lamports,
                    data: ProgramDataDeserialized {
                        slot,
                        upgrade_authority_address: upgrade_authority_address
                            .map(|pubkey| pubkey.to_string())
                            .unwrap_or_else(|| "none".to_string()),
                        raw_program_data: base64::encode(
                            &programdata_account.account.data
                                [UpgradeableLoaderState::size_of_programdata_metadata()..],
                        ),
                    },
                    owner: programdata_account.account.owner.to_string(),
                    executable: programdata_account.account.executable,
                    rent_epoch: programdata_account.account.rent_epoch,
                })
            } else {
                None
            },
        }
    }
}

impl fmt::Display for DisplayUpgradeableProgram {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "========================================================"
        )?;
        writeln!(f, "{} {}", style("Program Id:").bold(), self.program_id)?;
        writeln!(
            f,
            "========================================================"
        )?;

        if let Some(program_account) = &self.program_account {
            writeln!(f)?;

            writeln!(f, "{}", style("--> Program Account").bold(),)?;

            writeln!(f)?;

            writeln!(
                f,
                "{} {} (◎ {})",
                style("Lamports:").bold(),
                program_account.lamports,
                pretty_lamports_to_sol(program_account.lamports)
            )?;
            writeln!(
                f,
                "{} [Deserialized and interpreted below]",
                style("Data:").bold()
            )?;
            writeln!(f, "{} {}", style("Owner").bold(), program_account.owner)?;
            writeln!(
                f,
                "{} {}",
                style("Executable:").bold(),
                program_account.executable
            )?;
            writeln!(
                f,
                "{} {}",
                style("Rent Epoch:").bold(),
                program_account.rent_epoch
            )?;

            writeln!(f)?;

            writeln!(f, "{}", style("Deserialized:").bold())?;
            write!(f, "  - ")?;
            write!(
                f,
                "{} {}",
                style("ProgramData Address:").bold(),
                program_account.data.programdata_address
            )?;

            if self.programdata_account.is_some() {
                writeln!(f)?;
            }
        }

        if let Some(programdata_account) = &self.programdata_account {
            writeln!(f)?;

            writeln!(f, "{}", style("--> ProgramData Account").bold())?;

            writeln!(f)?;

            writeln!(
                f,
                "{} {} (◎ {})",
                style("Lamports:").bold(),
                programdata_account.lamports,
                pretty_lamports_to_sol(programdata_account.lamports)
            )?;
            writeln!(
                f,
                "{} [Deserialized and interpreted below]",
                style("Data:").bold()
            )?;
            writeln!(f, "{} {}", style("Owner").bold(), programdata_account.owner)?;
            writeln!(
                f,
                "{} {}",
                style("Executable:").bold(),
                programdata_account.executable
            )?;
            writeln!(
                f,
                "{} {}",
                style("Rent Epoch:").bold(),
                programdata_account.rent_epoch
            )?;

            writeln!(f)?;

            writeln!(f, "{}", style("Deserialized:").bold())?;
            write!(f, "  - ")?;
            writeln!(
                f,
                "{} {}",
                style("Last Deployed Slot:").bold(),
                programdata_account.data.slot
            )?;
            write!(f, "  - ")?;
            write!(
                f,
                "{} {}",
                style("Upgrade Authority:").bold(),
                programdata_account.data.upgrade_authority_address
            )?;
        }

        Ok(())
    }
}