2020-07-31 13:26:09 -06:00
|
|
|
use crate::parse_token::parse_token;
|
2020-06-19 16:15:13 -06:00
|
|
|
use inflector::Inflector;
|
2020-08-05 00:58:58 -06:00
|
|
|
use serde_json::Value;
|
2020-08-28 15:54:57 -06:00
|
|
|
use solana_account_decoder::parse_token::spl_token_id_v2_0;
|
2020-06-19 16:15:13 -06:00
|
|
|
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
str::{from_utf8, FromStr},
|
|
|
|
};
|
2020-07-31 13:26:09 -06:00
|
|
|
use thiserror::Error;
|
2020-06-19 16:15:13 -06:00
|
|
|
|
|
|
|
lazy_static! {
|
2020-07-28 23:00:48 -06:00
|
|
|
static ref MEMO_PROGRAM_ID: Pubkey =
|
|
|
|
Pubkey::from_str(&spl_memo_v1_0::id().to_string()).unwrap();
|
2020-08-28 15:54:57 -06:00
|
|
|
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();
|
2020-06-30 22:55:11 -06:00
|
|
|
static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableProgram> = {
|
2020-06-19 16:15:13 -06:00
|
|
|
let mut m = HashMap::new();
|
|
|
|
m.insert(*MEMO_PROGRAM_ID, ParsableProgram::SplMemo);
|
2020-07-31 13:26:09 -06:00
|
|
|
m.insert(*TOKEN_PROGRAM_ID, ParsableProgram::SplToken);
|
2020-06-19 16:15:13 -06:00
|
|
|
m
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-08-05 00:58:58 -06:00
|
|
|
#[derive(Error, Debug)]
|
2020-07-31 13:26:09 -06:00
|
|
|
pub enum ParseInstructionError {
|
|
|
|
#[error("{0:?} instruction not parsable")]
|
|
|
|
InstructionNotParsable(ParsableProgram),
|
|
|
|
|
|
|
|
#[error("{0:?} instruction key mismatch")]
|
|
|
|
InstructionKeyMismatch(ParsableProgram),
|
|
|
|
|
|
|
|
#[error("Program not parsable")]
|
|
|
|
ProgramNotParsable,
|
2020-08-05 00:58:58 -06:00
|
|
|
|
|
|
|
#[error("Internal error, please report")]
|
|
|
|
SerdeJsonError(#[from] serde_json::error::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct ParsedInstruction {
|
|
|
|
pub program: String,
|
|
|
|
pub program_id: String,
|
|
|
|
pub parsed: Value,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct ParsedInstructionEnum {
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
pub instruction_type: String,
|
|
|
|
pub info: Value,
|
2020-07-31 13:26:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
2020-06-19 16:15:13 -06:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2020-07-31 13:26:09 -06:00
|
|
|
pub enum ParsableProgram {
|
2020-06-19 16:15:13 -06:00
|
|
|
SplMemo,
|
2020-07-31 13:26:09 -06:00
|
|
|
SplToken,
|
2020-06-19 16:15:13 -06:00
|
|
|
}
|
|
|
|
|
2020-07-31 13:26:09 -06:00
|
|
|
pub fn parse(
|
|
|
|
program_id: &Pubkey,
|
|
|
|
instruction: &CompiledInstruction,
|
|
|
|
account_keys: &[Pubkey],
|
2020-08-05 00:58:58 -06:00
|
|
|
) -> Result<ParsedInstruction, ParseInstructionError> {
|
2020-07-31 13:26:09 -06:00
|
|
|
let program_name = PARSABLE_PROGRAM_IDS
|
|
|
|
.get(program_id)
|
|
|
|
.ok_or_else(|| ParseInstructionError::ProgramNotParsable)?;
|
|
|
|
let parsed_json = match program_name {
|
|
|
|
ParsableProgram::SplMemo => parse_memo(instruction),
|
2020-08-05 00:58:58 -06:00
|
|
|
ParsableProgram::SplToken => serde_json::to_value(parse_token(instruction, account_keys)?)?,
|
2020-07-31 13:26:09 -06:00
|
|
|
};
|
2020-08-05 00:58:58 -06:00
|
|
|
Ok(ParsedInstruction {
|
|
|
|
program: format!("{:?}", program_name).to_kebab_case(),
|
|
|
|
program_id: program_id.to_string(),
|
|
|
|
parsed: parsed_json,
|
|
|
|
})
|
2020-06-19 16:15:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_memo(instruction: &CompiledInstruction) -> Value {
|
|
|
|
Value::String(from_utf8(&instruction.data).unwrap().to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2020-08-05 00:58:58 -06:00
|
|
|
use serde_json::json;
|
2020-06-19 16:15:13 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse() {
|
|
|
|
let memo_instruction = CompiledInstruction {
|
|
|
|
program_id_index: 0,
|
|
|
|
accounts: vec![],
|
|
|
|
data: vec![240, 159, 166, 150],
|
|
|
|
};
|
|
|
|
assert_eq!(
|
2020-07-31 13:26:09 -06:00
|
|
|
parse(&MEMO_PROGRAM_ID, &memo_instruction, &[]).unwrap(),
|
2020-08-05 00:58:58 -06:00
|
|
|
ParsedInstruction {
|
|
|
|
program: "spl-memo".to_string(),
|
|
|
|
program_id: MEMO_PROGRAM_ID.to_string(),
|
|
|
|
parsed: json!("🦖"),
|
|
|
|
}
|
2020-06-19 16:15:13 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
let non_parsable_program_id = Pubkey::new(&[1; 32]);
|
2020-08-05 00:58:58 -06:00
|
|
|
assert!(parse(&non_parsable_program_id, &memo_instruction, &[]).is_err());
|
2020-06-19 16:15:13 -06:00
|
|
|
}
|
|
|
|
}
|