1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use phoenix::program::{load_with_dispatch, MarketHeader};
use phoenix::quantities::WrapperU64;
use phoenix::state::markets::{FIFOOrderId, FIFORestingOrder, RestingOrder};
use phoenix::state::Side;
use phoenix_sdk::sdk_client::*;
use solana_sdk::clock::Clock;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::sysvar;
use std::mem::size_of;

use crate::helpers::print_helpers::get_precision;

pub async fn process_get_open_orders(
    market_pubkey: &Pubkey,
    trader_pubkey: &Pubkey,
    sdk: &SDKClient,
) -> anyhow::Result<()> {
    let meta = sdk.get_market_metadata(market_pubkey).await?;
    // Get market account
    let mut market_and_clock = sdk
        .client
        .get_multiple_accounts_with_commitment(
            &[*market_pubkey, sysvar::clock::id()],
            CommitmentConfig::confirmed(),
        )
        .await?
        .value;

    let market_account_data = market_and_clock
        .remove(0)
        .ok_or_else(|| anyhow::Error::msg("Market account not found"))?
        .data;

    let clock_account_data = market_and_clock
        .remove(0)
        .ok_or_else(|| anyhow::Error::msg("Clock account not found"))?
        .data;

    let clock: Clock = bincode::deserialize(&clock_account_data)
        .map_err(|_| anyhow::Error::msg("Error deserializing clock"))?;

    let (header_bytes, market_bytes) = market_account_data.split_at(size_of::<MarketHeader>());
    let header: &MarketHeader = bytemuck::try_from_bytes(header_bytes)
        .map_err(|e| anyhow::anyhow!("Error getting market header. Error: {:?}", e))?;

    // Derserialize data and load into correct type
    let market = load_with_dispatch(&header.market_size_params, market_bytes)?.inner;

    let raw_base_units_per_base_lot =
        meta.base_atoms_per_base_lot as f64 / meta.base_atoms_per_raw_base_unit as f64;

    let trader_index = market
        .get_trader_index(trader_pubkey)
        .ok_or_else(|| anyhow::anyhow!("Trader not found"))?;
    let book_bids = market.get_book(Side::Bid);
    let book_asks = market.get_book(Side::Ask);
    let price_precision: usize = get_precision(
        10_u64.pow(meta.quote_decimals) / meta.tick_size_in_quote_atoms_per_base_unit,
    );
    let size_precision: usize = get_precision(meta.num_base_lots_per_base_unit);

    println!("Open Bids");
    let mut open_bids = vec![];
    open_bids.push(format!(
        "{0: <20} | {1: <20} | {2: <10} | {3: <10} | {4: <15} | {5: <15} ",
        "ID", "Price (ticks)", "Price", "Quantity", "Slots Remaining", "Seconds Remaining"
    ));
    for (order_id, order) in book_bids.iter() {
        if order.trader_index as u32 == trader_index {
            if order.is_expired(clock.slot, clock.unix_timestamp as u64) {
                continue;
            }
            open_bids.push(format_open_orders(
                sdk,
                market_pubkey,
                order_id,
                order,
                price_precision,
                size_precision,
                &clock,
                raw_base_units_per_base_lot,
            )?);
        }
    }
    open_bids.iter().for_each(|line| println!("{}", line));

    println!();
    println!("Open Asks");
    let mut open_asks = vec![];
    open_asks.push(format!(
        "{0: <20} | {1: <20} | {2: <10} | {3: <10} | {4: <15} | {5: <15} ",
        "ID", "Price (ticks)", "Price", "Quantity", "Slots Remaining", "Seconds Remaining"
    ));
    for (order_id, order) in book_asks.iter() {
        if order.trader_index as u32 == trader_index {
            if order.is_expired(clock.slot, clock.unix_timestamp as u64) {
                continue;
            }
            open_asks.push(format_open_orders(
                sdk,
                market_pubkey,
                order_id,
                order,
                price_precision,
                size_precision,
                &clock,
                raw_base_units_per_base_lot,
            )?);
        }
    }
    open_asks.iter().for_each(|line| println!("{}", line));

    Ok(())
}

#[allow(clippy::too_many_arguments)]
fn format_open_orders(
    sdk: &SDKClient,
    market_pubkey: &Pubkey,
    order_id: &FIFOOrderId,
    order: &FIFORestingOrder,
    price_precision: usize,
    size_precision: usize,
    clock: &Clock,
    raw_base_units_per_base_lot: f64,
) -> anyhow::Result<String> {
    Ok(format!(
        "{0: <20} | {1: <20} | {2: <10} | {3: <10} | {4: <15} | {5: <15} ",
        order_id.order_sequence_number as i64,
        order_id.price_in_ticks,
        format!(
            "{:.1$}",
            sdk.ticks_to_float_price(market_pubkey, order_id.price_in_ticks.as_u64())?,
            price_precision
        ),
        format!(
            "{:.1$}",
            order.num_base_lots.as_u64() as f64 * raw_base_units_per_base_lot,
            size_precision,
        ),
        if order.last_valid_slot >= clock.slot {
            (1 + order.last_valid_slot - clock.slot).to_string()
        } else {
            "∞".to_string()
        },
        if order.last_valid_unix_timestamp_in_seconds >= clock.unix_timestamp as u64 {
            (1 + order.last_valid_unix_timestamp_in_seconds - clock.unix_timestamp as u64)
                .to_string()
        } else {
            "∞".to_string()
        }
    ))
}