trident_explorer/
parse.rs1use 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 "11111111111111111111111111111111" => ParsableProgram::System,
39 "BPFLoader1111111111111111111111111111111111" => ParsableProgram::BPFLoaderDeprecated,
41 "BPFLoader2111111111111111111111111111111111" => ParsableProgram::BPFLoader,
43 "BPFLoaderUpgradeab1e11111111111111111111111" => ParsableProgram::BPFLoaderUpgradeable,
45 "Stake11111111111111111111111111111111111111" => ParsableProgram::Stake,
47 "Vote111111111111111111111111111111111111111" => ParsableProgram::Vote,
49 "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo" => ParsableProgram::SPLMemoV1,
51 "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" => ParsableProgram::SPLMemo,
53 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" => ParsableProgram::SPLToken,
55 "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}