Skip to main content

percli_chain/commands/
query.rs

1use anyhow::{bail, Result};
2
3use crate::config::ChainConfig;
4use crate::read;
5use crate::rpc::ChainRpc;
6
7pub enum QueryTarget {
8    Market,
9    Account(u16),
10}
11
12pub fn run(config: &ChainConfig, target: QueryTarget) -> Result<()> {
13    let rpc = ChainRpc::new(&config.rpc_url);
14    let (market_pda, _) = config.market_pda();
15
16    let data = rpc.get_account_data(&market_pda)?;
17    let engine = read::engine_from_data(&data)?;
18
19    match target {
20        QueryTarget::Market => print_market(engine, &market_pda, config),
21        QueryTarget::Account(idx) => print_account(engine, idx),
22    }
23}
24
25fn print_market(
26    engine: &percli_core::RiskEngine,
27    market_pda: &solana_sdk::pubkey::Pubkey,
28    config: &ChainConfig,
29) -> Result<()> {
30    let vault = engine.vault.get();
31    let insurance = engine.insurance_fund.balance.get();
32    let (h_num, h_den) = engine.haircut_ratio();
33    let conservation = engine.check_conservation();
34
35    println!("Market: {market_pda}");
36    println!("  Program:      {}", config.program_id);
37    println!("  Authority:    {}", config.authority());
38    println!("  Vault:        {vault}");
39    println!("  Insurance:    {insurance}");
40    println!("  Haircut:      {h_num}/{h_den}");
41    println!(
42        "  Conservation: {}",
43        if conservation { "PASS" } else { "FAIL" }
44    );
45    println!("  Oracle price: {}", engine.last_oracle_price);
46    println!("  Last slot:    {}", engine.last_crank_slot);
47
48    // Count active accounts
49    let mut active = 0u32;
50    for i in 0..engine.accounts.len() {
51        if engine.is_used(i) {
52            active += 1;
53        }
54    }
55    println!("  Accounts:     {active}/{}", engine.accounts.len());
56
57    Ok(())
58}
59
60fn print_account(engine: &percli_core::RiskEngine, idx: u16) -> Result<()> {
61    let i = idx as usize;
62    if i >= engine.accounts.len() {
63        bail!(
64            "Account index {idx} out of range (max {})",
65            engine.accounts.len() - 1
66        );
67    }
68    if !engine.is_used(i) {
69        bail!("Account slot {idx} is empty");
70    }
71
72    let acct = &engine.accounts[i];
73    let eff_pos = engine.effective_pos_q(i);
74    let equity_maint = engine.account_equity_maint_raw(acct);
75    let equity_init = engine.account_equity_init_raw(acct, i);
76    let above_mm = engine.is_above_maintenance_margin(acct, i, engine.last_oracle_price);
77    let above_im = engine.is_above_initial_margin(acct, i, engine.last_oracle_price);
78    let notional = engine.notional(i, engine.last_oracle_price);
79
80    println!("Account #{idx}");
81    println!("  Capital:    {}", acct.capital.get());
82    println!("  PnL:        {}", acct.pnl);
83    println!("  Reserved:   {}", acct.reserved_pnl);
84    println!("  Position:   {eff_pos}");
85    println!("  Notional:   {notional}");
86    println!("  Equity(M):  {equity_maint}");
87    println!("  Equity(I):  {equity_init}");
88    println!("  Above MM:   {above_mm}");
89    println!("  Above IM:   {above_im}");
90
91    Ok(())
92}