solana_binary_encoder/parse_instruction.rs
1
2use {
3 // crate::{
4 // // pubkey::Pubkey,
5 // // instruction::CompiledInstruction,
6 // // account_keys::AccountKeys,
7 // // parse_address_lookup_table::parse_address_lookup_table,
8 // },
9 solana_sdk::{
10 instruction::CompiledInstruction,
11 message::AccountKeys,
12 pubkey::Pubkey
13 },
14 serde_derive::{Serialize, Deserialize},
15 serde_json::Value,
16 thiserror::Error,
17 inflector::Inflector,
18 lazy_static::lazy_static,
19 std::{
20 collections::HashMap,
21 str::{from_utf8, Utf8Error},
22 },
23};
24
25lazy_static! {
26 // static ref ADDRESS_LOOKUP_PROGRAM_ID: Pubkey = solana_address_lookup_table_program::id();
27 // static ref ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = spl_associated_token_id();
28 // static ref BPF_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader::id();
29 // static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id();
30 // static ref MEMO_V1_PROGRAM_ID: Pubkey = spl_memo_id_v1();
31 // static ref MEMO_V3_PROGRAM_ID: Pubkey = spl_memo_id_v3();
32 // static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id();
33 // static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
34 // static ref VOTE_PROGRAM_ID: Pubkey = vote::program::id();
35 static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableProgram> = {
36 let m = HashMap::new();
37 // let mut m = HashMap::new();
38 // m.insert(
39 // *ADDRESS_LOOKUP_PROGRAM_ID,
40 // ParsableProgram::AddressLookupTable,
41 // );
42 // m.insert(
43 // *ASSOCIATED_TOKEN_PROGRAM_ID,
44 // ParsableProgram::SplAssociatedTokenAccount,
45 // );
46 // m.insert(*MEMO_V1_PROGRAM_ID, ParsableProgram::SplMemo);
47 // m.insert(*MEMO_V3_PROGRAM_ID, ParsableProgram::SplMemo);
48 // for spl_token_id in spl_token_ids() {
49 // m.insert(spl_token_id, ParsableProgram::SplToken);
50 // }
51 // m.insert(*BPF_LOADER_PROGRAM_ID, ParsableProgram::BpfLoader);
52 // m.insert(
53 // *BPF_UPGRADEABLE_LOADER_PROGRAM_ID,
54 // ParsableProgram::BpfUpgradeableLoader,
55 // );
56 // m.insert(*STAKE_PROGRAM_ID, ParsableProgram::Stake);
57 // m.insert(*SYSTEM_PROGRAM_ID, ParsableProgram::System);
58 // m.insert(*VOTE_PROGRAM_ID, ParsableProgram::Vote);
59 m
60 };
61}
62
63#[derive(Error, Debug)]
64pub enum ParseInstructionError {
65 #[error("{0:?} instruction not parsable")]
66 InstructionNotParsable(ParsableProgram),
67
68 // #[error("{0:?} instruction key mismatch")]
69 // InstructionKeyMismatch(ParsableProgram),
70
71 #[error("Program not parsable")]
72 ProgramNotParsable,
73
74 #[error("Internal error, please report")]
75 SerdeJsonError(#[from] serde_json::error::Error),
76}
77
78#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
79#[serde(rename_all = "camelCase")]
80pub struct ParsedInstruction {
81 pub program: String,
82 pub program_id: String,
83 pub parsed: Value,
84 pub stack_height: Option<u32>,
85}
86
87#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
88#[serde(rename_all = "camelCase")]
89pub struct ParsedInstructionEnum {
90 #[serde(rename = "type")]
91 pub instruction_type: String,
92 #[serde(default, skip_serializing_if = "Value::is_null")]
93 pub info: Value,
94}
95
96#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
97#[serde(rename_all = "camelCase")]
98pub enum ParsableProgram {
99 // AddressLookupTable,
100 // SplAssociatedTokenAccount,
101 SplMemo,
102 // SplToken,
103 // BpfLoader,
104 // BpfUpgradeableLoader,
105 // Stake,
106 // System,
107 // Vote,
108}
109
110pub fn parse(
111 program_id: &Pubkey,
112 instruction: &CompiledInstruction,
113 _account_keys: &AccountKeys,
114 stack_height: Option<u32>,
115) -> Result<ParsedInstruction, ParseInstructionError> {
116 let program_name = PARSABLE_PROGRAM_IDS
117 .get(program_id)
118 .ok_or(ParseInstructionError::ProgramNotParsable)?;
119 let parsed_json = match program_name {
120 // ParsableProgram::AddressLookupTable => {
121 // serde_json::to_value(parse_address_lookup_table(instruction, account_keys)?)?
122 // }
123 // ParsableProgram::SplAssociatedTokenAccount => {
124 // serde_json::to_value(parse_associated_token(instruction, account_keys)?)?
125 // }
126 ParsableProgram::SplMemo => parse_memo(instruction)?,
127 // ParsableProgram::SplToken => serde_json::to_value(parse_token(instruction, account_keys)?)?,
128 // ParsableProgram::BpfLoader => {
129 // serde_json::to_value(parse_bpf_loader(instruction, account_keys)?)?
130 // }
131 // ParsableProgram::BpfUpgradeableLoader => {
132 // serde_json::to_value(parse_bpf_upgradeable_loader(instruction, account_keys)?)?
133 // }
134 // ParsableProgram::Stake => serde_json::to_value(parse_stake(instruction, account_keys)?)?,
135 // ParsableProgram::System => serde_json::to_value(parse_system(instruction, account_keys)?)?,
136 // ParsableProgram::Vote => serde_json::to_value(parse_vote(instruction, account_keys)?)?,
137 };
138 Ok(ParsedInstruction {
139 program: format!("{:?}", program_name).to_kebab_case(),
140 program_id: program_id.to_string(),
141 parsed: parsed_json,
142 stack_height,
143 })
144}
145
146fn parse_memo(instruction: &CompiledInstruction) -> Result<Value, ParseInstructionError> {
147 parse_memo_data(&instruction.data)
148 .map(Value::String)
149 .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplMemo))
150}
151
152pub fn parse_memo_data(data: &[u8]) -> Result<String, Utf8Error> {
153 from_utf8(data).map(|s| s.to_string())
154}