gemachain_account_decoder/
parse_account_data.rs1use crate::{
2 parse_bpf_loader::parse_bpf_upgradeable_loader,
3 parse_config::parse_config,
4 parse_nonce::parse_nonce,
5 parse_stake::parse_stake,
6 parse_sysvar::parse_sysvar,
7 parse_token::{parse_token, spl_token_id_v2_0},
8 parse_vote::parse_vote,
9};
10use inflector::Inflector;
11use serde_json::Value;
12use gemachain_sdk::{instruction::InstructionError, pubkey::Pubkey, stake, system_program, sysvar};
13use std::collections::HashMap;
14use thiserror::Error;
15
16lazy_static! {
17 static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = gemachain_sdk::bpf_loader_upgradeable::id();
18 static ref CONFIG_PROGRAM_ID: Pubkey = gemachain_config_program::id();
19 static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id();
20 static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
21 static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id();
22 static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();
23 static ref VOTE_PROGRAM_ID: Pubkey = gemachain_vote_program::id();
24 pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
25 let mut m = HashMap::new();
26 m.insert(
27 *BPF_UPGRADEABLE_LOADER_PROGRAM_ID,
28 ParsableAccount::BpfUpgradeableLoader,
29 );
30 m.insert(*CONFIG_PROGRAM_ID, ParsableAccount::Config);
31 m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
32 m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::SplToken);
33 m.insert(*STAKE_PROGRAM_ID, ParsableAccount::Stake);
34 m.insert(*SYSVAR_PROGRAM_ID, ParsableAccount::Sysvar);
35 m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
36 m
37 };
38}
39
40#[derive(Error, Debug)]
41pub enum ParseAccountError {
42 #[error("{0:?} account not parsable")]
43 AccountNotParsable(ParsableAccount),
44
45 #[error("Program not parsable")]
46 ProgramNotParsable,
47
48 #[error("Additional data required to parse: {0}")]
49 AdditionalDataMissing(String),
50
51 #[error("Instruction error")]
52 InstructionError(#[from] InstructionError),
53
54 #[error("Serde json error")]
55 SerdeJsonError(#[from] serde_json::error::Error),
56}
57
58#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
59#[serde(rename_all = "camelCase")]
60pub struct ParsedAccount {
61 pub program: String,
62 pub parsed: Value,
63 pub space: u64,
64}
65
66#[derive(Debug, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase")]
68pub enum ParsableAccount {
69 BpfUpgradeableLoader,
70 Config,
71 Nonce,
72 SplToken,
73 Stake,
74 Sysvar,
75 Vote,
76}
77
78#[derive(Default)]
79pub struct AccountAdditionalData {
80 pub spl_token_decimals: Option<u8>,
81}
82
83pub fn parse_account_data(
84 pubkey: &Pubkey,
85 program_id: &Pubkey,
86 data: &[u8],
87 additional_data: Option<AccountAdditionalData>,
88) -> Result<ParsedAccount, ParseAccountError> {
89 let program_name = PARSABLE_PROGRAM_IDS
90 .get(program_id)
91 .ok_or(ParseAccountError::ProgramNotParsable)?;
92 let additional_data = additional_data.unwrap_or_default();
93 let parsed_json = match program_name {
94 ParsableAccount::BpfUpgradeableLoader => {
95 serde_json::to_value(parse_bpf_upgradeable_loader(data)?)?
96 }
97 ParsableAccount::Config => serde_json::to_value(parse_config(data, pubkey)?)?,
98 ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
99 ParsableAccount::SplToken => {
100 serde_json::to_value(parse_token(data, additional_data.spl_token_decimals)?)?
101 }
102 ParsableAccount::Stake => serde_json::to_value(parse_stake(data)?)?,
103 ParsableAccount::Sysvar => serde_json::to_value(parse_sysvar(data, pubkey)?)?,
104 ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
105 };
106 Ok(ParsedAccount {
107 program: format!("{:?}", program_name).to_kebab_case(),
108 parsed: parsed_json,
109 space: data.len() as u64,
110 })
111}
112
113#[cfg(test)]
114mod test {
115 use super::*;
116 use gemachain_sdk::nonce::{
117 state::{Data, Versions},
118 State,
119 };
120 use gemachain_vote_program::vote_state::{VoteState, VoteStateVersions};
121
122 #[test]
123 fn test_parse_account_data() {
124 let account_pubkey = gemachain_sdk::pubkey::new_rand();
125 let other_program = gemachain_sdk::pubkey::new_rand();
126 let data = vec![0; 4];
127 assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
128
129 let vote_state = VoteState::default();
130 let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
131 let versioned = VoteStateVersions::new_current(vote_state);
132 VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
133 let parsed = parse_account_data(
134 &account_pubkey,
135 &gemachain_vote_program::id(),
136 &vote_account_data,
137 None,
138 )
139 .unwrap();
140 assert_eq!(parsed.program, "vote".to_string());
141 assert_eq!(parsed.space, VoteState::size_of() as u64);
142
143 let nonce_data = Versions::new_current(State::Initialized(Data::default()));
144 let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
145 let parsed = parse_account_data(
146 &account_pubkey,
147 &system_program::id(),
148 &nonce_account_data,
149 None,
150 )
151 .unwrap();
152 assert_eq!(parsed.program, "nonce".to_string());
153 assert_eq!(parsed.space, State::size() as u64);
154 }
155}