use crate::base58::Pubkey;
use crate::durable_nonce::{parse_nonce_account, NonceState, NonceVersion};
use crate::rpc::{AccountInfo, TxSummary};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AccountKind {
Nonce,
Attestation,
Other,
}
pub fn estimate_tokens(s: &str) -> usize {
s.chars().count() / 4
}
pub fn short_pubkey(pk: &Pubkey) -> String {
let s = pk.to_string();
let bytes = s.as_bytes();
if bytes.len() <= 8 {
return s;
}
format!("{}…{}", &s[..4], &s[s.len() - 4..])
}
pub fn shape_account_info(info: &AccountInfo, kind: AccountKind) -> String {
match kind {
AccountKind::Nonce => shape_nonce_account(info),
AccountKind::Attestation => format!(
"Attestation account owner={} len={}B lamports={} data[0:8]={}",
short_pubkey(&info.owner),
info.data.len(),
info.lamports,
hex_prefix(&info.data, 8),
),
AccountKind::Other => format!(
"Account owner={} len={}B lamports={} executable={} data[0:8]={}",
short_pubkey(&info.owner),
info.data.len(),
info.lamports,
info.executable,
hex_prefix(&info.data, 8),
),
}
}
fn shape_nonce_account(info: &AccountInfo) -> String {
match parse_nonce_account(&info.data) {
Ok(nonce) => match nonce.version {
NonceVersion::Legacy => "Nonce: legacy (unsupported for versioned tx)".to_string(),
NonceVersion::Current => match nonce.state {
NonceState::Uninitialized => "Nonce: current/uninitialized".to_string(),
NonceState::Initialized(d) => format!(
"Nonce: current/initialized auth={} nonce={} lamports_per_sig={}",
short_pubkey(&d.authority),
short_pubkey(&Pubkey::from_bytes(d.durable_nonce)),
d.lamports_per_signature,
),
},
},
Err(e) => format!(
"Nonce: parse error ({:?}) owner={} len={}B data[0:8]={}",
e,
short_pubkey(&info.owner),
info.data.len(),
hex_prefix(&info.data, 8),
),
}
}
pub fn shape_signatures(sigs: &[TxSummary]) -> String {
if sigs.is_empty() {
return "No recent transactions.".to_string();
}
let total = sigs.len();
const SHOW: usize = 5;
let head: Vec<String> = sigs.iter().take(SHOW).map(|t| {
let status = match &t.err {
None => "ok".to_string(),
Some(e) => format!("FAIL:{}", truncate(e, 20)),
};
let sig_short = truncate(&t.signature, 8);
format!("{}(slot {}, {})", sig_short, t.slot, status)
}).collect();
let mut out = format!("{} txs: {}", total, head.join(" | "));
if total > SHOW {
out.push_str(&format!(" | +{} more", total - SHOW));
}
out
}
pub fn shape_attestation_result(sig: &str, pda: &Pubkey, nonce: &Pubkey) -> String {
format!(
"Attested → PDA {} (nonce {}) sig {}",
short_pubkey(pda),
short_pubkey(nonce),
truncate(sig, 12),
)
}
fn hex_prefix(data: &[u8], n: usize) -> String {
let n = n.min(data.len());
let mut s = String::with_capacity(n * 2);
for b in data.iter().take(n) {
s.push_str(&format!("{:02x}", b));
}
if data.len() > n {
s.push('…');
}
s
}
fn truncate(s: &str, max: usize) -> String {
if s.chars().count() <= max {
return s.to_string();
}
let head: String = s.chars().take(max).collect();
format!("{head}…")
}