Rework parsed account format (#11372)
* Rework parsed account format * Serialize as type
This commit is contained in:
@ -8,8 +8,7 @@ pub mod parse_nonce;
|
|||||||
pub mod parse_token;
|
pub mod parse_token;
|
||||||
pub mod parse_vote;
|
pub mod parse_vote;
|
||||||
|
|
||||||
use crate::parse_account_data::parse_account_data;
|
use crate::parse_account_data::{parse_account_data, ParsedAccount};
|
||||||
use serde_json::Value;
|
|
||||||
use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey};
|
use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ pub struct UiAccount {
|
|||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum UiAccountData {
|
pub enum UiAccountData {
|
||||||
Binary(String),
|
Binary(String),
|
||||||
Json(Value),
|
Json(ParsedAccount),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for UiAccountData {
|
impl From<Vec<u8>> for UiAccountData {
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
parse_vote::parse_vote,
|
parse_vote::parse_vote,
|
||||||
};
|
};
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use serde_json::{json, Value};
|
use serde_json::Value;
|
||||||
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program};
|
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@ -37,6 +37,13 @@ pub enum ParseAccountError {
|
|||||||
SerdeJsonError(#[from] serde_json::error::Error),
|
SerdeJsonError(#[from] serde_json::error::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ParsedAccount {
|
||||||
|
pub program: String,
|
||||||
|
pub parsed: Value,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum ParsableAccount {
|
pub enum ParsableAccount {
|
||||||
@ -45,7 +52,10 @@ pub enum ParsableAccount {
|
|||||||
Vote,
|
Vote,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result<Value, ParseAccountError> {
|
pub fn parse_account_data(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<ParsedAccount, ParseAccountError> {
|
||||||
let program_name = PARSABLE_PROGRAM_IDS
|
let program_name = PARSABLE_PROGRAM_IDS
|
||||||
.get(program_id)
|
.get(program_id)
|
||||||
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
|
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
|
||||||
@ -54,9 +64,10 @@ pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result<Value, Par
|
|||||||
ParsableAccount::SplToken => serde_json::to_value(parse_token(data)?)?,
|
ParsableAccount::SplToken => serde_json::to_value(parse_token(data)?)?,
|
||||||
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
|
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
|
||||||
};
|
};
|
||||||
Ok(json!({
|
Ok(ParsedAccount {
|
||||||
format!("{:?}", program_name).to_kebab_case(): parsed_json
|
program: format!("{:?}", program_name).to_kebab_case(),
|
||||||
}))
|
parsed: parsed_json,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -79,11 +90,11 @@ mod test {
|
|||||||
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
||||||
VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
|
VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
|
||||||
let parsed = parse_account_data(&solana_vote_program::id(), &vote_account_data).unwrap();
|
let parsed = parse_account_data(&solana_vote_program::id(), &vote_account_data).unwrap();
|
||||||
assert!(parsed.as_object().unwrap().contains_key("vote"));
|
assert_eq!(parsed.program, "vote".to_string());
|
||||||
|
|
||||||
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
|
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
|
||||||
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
|
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
|
||||||
let parsed = parse_account_data(&system_program::id(), &nonce_account_data).unwrap();
|
let parsed = parse_account_data(&system_program::id(), &nonce_account_data).unwrap();
|
||||||
assert!(parsed.as_object().unwrap().contains_key("nonce"));
|
assert_eq!(parsed.program, "nonce".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
|
|||||||
|
|
||||||
/// A duplicate representation of NonceState for pretty JSON serialization
|
/// A duplicate representation of NonceState for pretty JSON serialization
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
||||||
pub enum UiNonceState {
|
pub enum UiNonceState {
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
Initialized(UiNonceData),
|
Initialized(UiNonceData),
|
||||||
|
@ -74,7 +74,7 @@ pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
||||||
pub enum TokenAccountType {
|
pub enum TokenAccountType {
|
||||||
Account(UiTokenAccount),
|
Account(UiTokenAccount),
|
||||||
Mint(UiMint),
|
Mint(UiMint),
|
||||||
|
@ -5,7 +5,7 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState};
|
use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState};
|
||||||
|
|
||||||
pub fn parse_vote(data: &[u8]) -> Result<UiVoteState, ParseAccountError> {
|
pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
|
||||||
let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?;
|
let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?;
|
||||||
let epoch_credits = vote_state
|
let epoch_credits = vote_state
|
||||||
.epoch_credits()
|
.epoch_credits()
|
||||||
@ -45,7 +45,7 @@ pub fn parse_vote(data: &[u8]) -> Result<UiVoteState, ParseAccountError> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
Ok(UiVoteState {
|
Ok(VoteAccountType::Vote(UiVoteState {
|
||||||
node_pubkey: vote_state.node_pubkey.to_string(),
|
node_pubkey: vote_state.node_pubkey.to_string(),
|
||||||
authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
|
authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
|
||||||
commission: vote_state.commission,
|
commission: vote_state.commission,
|
||||||
@ -55,7 +55,14 @@ pub fn parse_vote(data: &[u8]) -> Result<UiVoteState, ParseAccountError> {
|
|||||||
prior_voters,
|
prior_voters,
|
||||||
epoch_credits,
|
epoch_credits,
|
||||||
last_timestamp: vote_state.last_timestamp,
|
last_timestamp: vote_state.last_timestamp,
|
||||||
})
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper enum for consistency across programs
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
||||||
|
pub enum VoteAccountType {
|
||||||
|
Vote(UiVoteState),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A duplicate representation of VoteState for pretty JSON serialization
|
/// A duplicate representation of VoteState for pretty JSON serialization
|
||||||
@ -126,7 +133,10 @@ mod test {
|
|||||||
let mut expected_vote_state = UiVoteState::default();
|
let mut expected_vote_state = UiVoteState::default();
|
||||||
expected_vote_state.node_pubkey = Pubkey::default().to_string();
|
expected_vote_state.node_pubkey = Pubkey::default().to_string();
|
||||||
expected_vote_state.authorized_withdrawer = Pubkey::default().to_string();
|
expected_vote_state.authorized_withdrawer = Pubkey::default().to_string();
|
||||||
assert_eq!(parse_vote(&vote_account_data).unwrap(), expected_vote_state,);
|
assert_eq!(
|
||||||
|
parse_vote(&vote_account_data).unwrap(),
|
||||||
|
VoteAccountType::Vote(expected_vote_state)
|
||||||
|
);
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
let bad_data = vec![0; 4];
|
||||||
assert!(parse_vote(&bad_data).is_err());
|
assert!(parse_vote(&bad_data).is_err());
|
||||||
|
@ -1079,7 +1079,7 @@ The result will be an RpcResponse JSON object with `value` equal to an array of
|
|||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByDelegate", "params": ["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", {"programId": "TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"}, {"encoding": "jsonParsed"}]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByDelegate", "params": ["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", {"programId": "TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"}, {"encoding": "jsonParsed"}]}' http://localhost:8899
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"token":{"account":{"amount":1,"delegate":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1}
|
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"program":"spl-token","parsed":{"accountType":"account","info":{"amount":1,"delegate":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getTokenAccountsByOwner
|
### getTokenAccountsByOwner
|
||||||
@ -1115,7 +1115,7 @@ The result will be an RpcResponse JSON object with `value` equal to an array of
|
|||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByOwner", "params": ["4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F", {"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"}, {"encoding": "jsonParsed"}]}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByOwner", "params": ["4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F", {"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"}, {"encoding": "jsonParsed"}]}' http://localhost:8899
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"token":{"account":{"amount":1,"delegate":null,"delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1}
|
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"program":"spl-token","parsed":{"accountType":"account","info":{"amount":1,"delegate":null,"delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getTokenSupply
|
### getTokenSupply
|
||||||
|
Reference in New Issue
Block a user