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}