trident_explorer/
parse.rs

1use self::{
2    associated_token_account::parse_associated_token_account, bpf_loader::parse_bpf_loader,
3    bpf_upgradeable_loader::parse_bpf_upgradeable_loader, memo::parse_memo, stake::parse_stake,
4    system::parse_system, token::parse_token, vote::parse_vote,
5};
6use crate::transaction::{DisplayParsedInstruction, DisplayPartiallyParsedInstruction};
7use phf::phf_map;
8use serde::Serialize;
9use serde_json::Value;
10use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
11use thiserror::Error;
12
13mod associated_token_account;
14mod bpf_loader;
15mod bpf_upgradeable_loader;
16mod memo;
17mod stake;
18mod system;
19mod token;
20mod vote;
21
22#[derive(Clone, Debug)]
23pub enum ParsableProgram {
24    System,
25    BPFLoaderDeprecated,
26    BPFLoader,
27    BPFLoaderUpgradeable,
28    Stake,
29    Vote,
30    SPLMemoV1,
31    SPLMemo,
32    SPLToken,
33    SPLAssociatedTokenAccount,
34}
35
36static PARSABLE_PROGRAM_IDS: phf::Map<&'static str, ParsableProgram> = phf_map! {
37     // System
38     "11111111111111111111111111111111" => ParsableProgram::System,
39     // BPF Loader Deprecated
40     "BPFLoader1111111111111111111111111111111111" => ParsableProgram::BPFLoaderDeprecated,
41     // BPF Loader
42     "BPFLoader2111111111111111111111111111111111" => ParsableProgram::BPFLoader,
43     // BPF Loader Upgradeable
44     "BPFLoaderUpgradeab1e11111111111111111111111" => ParsableProgram::BPFLoaderUpgradeable,
45     // Stake
46     "Stake11111111111111111111111111111111111111" => ParsableProgram::Stake,
47     // Vote
48     "Vote111111111111111111111111111111111111111" => ParsableProgram::Vote,
49     // SPL Memo v1
50     "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo" => ParsableProgram::SPLMemoV1,
51     // SPL Memo (current)
52     "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" => ParsableProgram::SPLMemo,
53     // SPL Token
54     "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" => ParsableProgram::SPLToken,
55     // SPL Associated Token Account
56     "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" => ParsableProgram::SPLAssociatedTokenAccount
57};
58
59#[derive(Error, Debug)]
60pub enum ParseInstructionError {
61    #[error("{0:?} instruction not parsable")]
62    InstructionNotParsable(ParsableProgram),
63
64    #[error("{0:?} instruction key mismatch")]
65    InstructionKeyMismatch(ParsableProgram),
66
67    #[error("Program not parsable")]
68    ProgramNotParsable,
69
70    #[error("Internal error, please report")]
71    SerdeJsonError(#[from] serde_json::error::Error),
72}
73
74#[derive(Serialize, PartialEq, Eq, Debug)]
75pub struct ParsedInstructionEnum {
76    #[serde(rename = "type")]
77    pub instruction_type: String,
78    #[serde(skip_serializing_if = "Value::is_null")]
79    pub info: Value,
80}
81
82pub fn parse(
83    program_id: &Pubkey,
84    instruction: &CompiledInstruction,
85    account_keys: &[Pubkey],
86) -> Result<DisplayParsedInstruction, ParseInstructionError> {
87    let program_name = PARSABLE_PROGRAM_IDS
88        .get(&program_id.to_string())
89        .ok_or(ParseInstructionError::ProgramNotParsable)?;
90
91    let parsed_json = match program_name {
92        ParsableProgram::System => serde_json::to_value(parse_system(instruction, account_keys)?)?,
93        ParsableProgram::BPFLoaderDeprecated | ParsableProgram::BPFLoader => {
94            serde_json::to_value(parse_bpf_loader(instruction, account_keys)?)?
95        }
96        ParsableProgram::BPFLoaderUpgradeable => {
97            serde_json::to_value(parse_bpf_upgradeable_loader(instruction, account_keys)?)?
98        }
99        ParsableProgram::Stake => serde_json::to_value(parse_stake(instruction, account_keys)?)?,
100        ParsableProgram::Vote => serde_json::to_value(parse_vote(instruction, account_keys)?)?,
101        ParsableProgram::SPLMemoV1 | ParsableProgram::SPLMemo => {
102            serde_json::to_value(parse_memo(instruction)?)?
103        }
104        ParsableProgram::SPLToken => serde_json::to_value(parse_token(instruction, account_keys)?)?,
105        ParsableProgram::SPLAssociatedTokenAccount => {
106            serde_json::to_value(parse_associated_token_account(instruction, account_keys)?)?
107        }
108    };
109
110    Ok(DisplayParsedInstruction {
111        program: format!("{program_name:?}"),
112        program_id: program_id.to_string(),
113        parsed: parsed_json,
114    })
115}
116
117pub fn check_num_accounts(
118    accounts: &[u8],
119    num: usize,
120    parsable_program: ParsableProgram,
121) -> Result<(), ParseInstructionError> {
122    if accounts.len() < num {
123        Err(ParseInstructionError::InstructionKeyMismatch(
124            parsable_program,
125        ))
126    } else {
127        Ok(())
128    }
129}
130
131pub fn partially_parse(
132    program_id: &Pubkey,
133    instruction: &CompiledInstruction,
134    account_keys: &[Pubkey],
135) -> DisplayPartiallyParsedInstruction {
136    DisplayPartiallyParsedInstruction {
137        program_id: program_id.to_string(),
138        accounts: instruction
139            .accounts
140            .iter()
141            .map(|&i| account_keys[i as usize].to_string())
142            .collect(),
143        data: bs58::encode(instruction.data.clone()).into_string(),
144    }
145}