use serde::Serialize;
use crate::{compute, cpi_tree, decode, diffs, replay};
#[derive(Debug, Serialize)]
pub struct Analysis {
pub signature: String,
pub overview: Overview,
pub cpi_tree: Vec<cpi_tree::CpiEntry>,
pub balance_change: Vec<diffs::BalanceChange>,
pub token_change: Vec<diffs::TokenChange>,
pub compute: Vec<compute::CuUsage>,
pub logs: Vec<String>,
pub replay: Option<replay::ReplayResult>,
pub accounts: Vec<decode::AccountInfo>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Overview {
pub success: bool,
pub fee: u64,
pub slot: Option<u64>,
pub compute_units: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fee_payer: Option<String>,
pub version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub block_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recent_blockhash: Option<String>,
pub account_count: usize,
pub top_programs: Vec<String>,
}
pub(crate) fn build_overview(
tx: &serde_json::Value,
cpi_tree: &[cpi_tree::CpiEntry],
account_count: usize,
) -> Overview {
let top_programs = cpi_tree
.iter()
.filter(|e| e.stack_height == 1)
.map(|e| e.program.clone())
.collect();
let version = match &tx["version"] {
serde_json::Value::Number(n) => format!("v{n}"),
serde_json::Value::String(s) => s.clone(),
_ => "legacy".to_string(),
};
Overview {
success: tx["meta"]["err"].is_null(),
fee: tx["meta"]["fee"].as_u64().unwrap_or(0),
slot: tx["slot"].as_u64(),
compute_units: tx["meta"]["computeUnitsConsumed"].as_u64(),
fee_payer: tx["transaction"]["message"]["accountKeys"][0]
.as_str()
.map(String::from),
version,
block_time: tx["blockTime"].as_i64(),
recent_blockhash: tx["transaction"]["message"]["recentBlockhash"]
.as_str()
.map(String::from),
account_count,
top_programs,
}
}
#[derive(Debug, Serialize)]
pub struct AccountOverview {
pub address: String,
pub exists: bool,
pub owner: String,
pub lamports: u64,
pub executable: bool,
pub data_len: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub program: Option<ProgramInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub idl_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decoded: Option<decode::DecodedAccount>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ProgramInfo {
pub program_data: String,
pub upgradeable: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub upgrade_authority: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_deployed_slot: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct SigInfo {
pub signature: String,
pub slot: Option<u64>,
pub err: bool,
pub block_time: Option<i64>,
}
#[derive(Debug, Serialize)]
pub struct SimulationReport {
pub replay: replay::ReplayResult,
#[serde(skip_serializing_if = "Option::is_none")]
pub clock: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub explain: Option<Explanation>,
pub diffs: Vec<AccountDiff>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Explanation {
pub title: String,
pub detail: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub program: Option<String>,
pub raw: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct AccountDiff {
pub address: String,
pub owner: String,
pub lamports_before: u64,
pub lamports_after: u64,
pub fields: Vec<FieldDiff>,
pub raw_data_changed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FieldDiff {
pub name: String,
#[serde(rename = "type")]
pub ty: String,
pub before: String,
pub after: String,
}