Skip to main content

gmsol_cli/commands/
utils.rs

1use eyre::OptionExt;
2use gmsol_sdk::{
3    client::token_map::TokenMap,
4    core::token_config::TokenMapAccess,
5    programs::{anchor_lang::prelude::Pubkey, gmsol_store::accounts::Market},
6    solana_utils::solana_sdk::{signature::Keypair, signer::EncodableKey},
7    utils::{market::MarketDecimals, unsigned_amount_to_decimal, Amount, Value},
8};
9use rand::{rngs::StdRng, SeedableRng};
10use rust_decimal::{Decimal, MathematicalOps};
11
12pub(crate) fn get_token_amount_with_token_map(
13    amount: &Amount,
14    token: &Pubkey,
15    token_map: &TokenMap,
16) -> eyre::Result<u64> {
17    let decimals = token_map
18        .get(token)
19        .ok_or_eyre("token config not found")?
20        .token_decimals;
21    Ok(amount.to_u64(decimals)?)
22}
23
24pub(crate) fn token_amount(
25    amount: &Amount,
26    token: Option<&Pubkey>,
27    token_map: &TokenMap,
28    market: &Market,
29    is_long: bool,
30) -> eyre::Result<u64> {
31    let token = match token {
32        Some(token) => token,
33        None => {
34            if is_long {
35                &market.meta.long_token_mint
36            } else {
37                &market.meta.short_token_mint
38            }
39        }
40    };
41    get_token_amount_with_token_map(amount, token, token_map)
42}
43
44pub(crate) fn unit_price(
45    price: &Value,
46    token_map: &TokenMap,
47    market: &Market,
48) -> eyre::Result<u128> {
49    let decimals = MarketDecimals::new(&market.meta.into(), token_map)?;
50    let mut price = *price;
51    price.0 /= Decimal::TEN.powu(decimals.index_token_decimals.into());
52
53    Ok(price.to_u128()?)
54}
55
56/// Price to min output amount.
57pub(crate) fn price_to_min_output_amount(
58    token_map: &TokenMap,
59    token_in: &Pubkey,
60    token_in_amount: u64,
61    token_out: &Pubkey,
62    token_in_to_token_out_price: Value,
63) -> Option<u64> {
64    let token_in = token_map.get(token_in)?;
65    let token_in_amount = unsigned_amount_to_decimal(token_in_amount, token_in.token_decimals());
66    let token_out_amount = Amount(token_in_amount.checked_div(token_in_to_token_out_price.0)?);
67    let token_out = token_map.get(token_out)?;
68    token_out_amount.to_u64(token_out.token_decimals).ok()
69}
70
71pub(crate) fn toml_from_file<T>(path: &impl AsRef<std::path::Path>) -> eyre::Result<T>
72where
73    T: serde::de::DeserializeOwned,
74{
75    use std::io::Read;
76
77    let mut buffer = String::new();
78    std::fs::File::open(path)?.read_to_string(&mut buffer)?;
79    Ok(toml::from_str(&buffer)?)
80}
81
82#[derive(Debug, clap::Args)]
83pub(crate) struct KeypairArgs {
84    /// Path to the keypair of the account to use.
85    /// If not provided, a new keypair will be generated.
86    keypair: Option<std::path::PathBuf>,
87    /// Optional random seed to use for keypair generation.
88    #[arg(long)]
89    seed: Option<u64>,
90}
91
92impl KeypairArgs {
93    pub(crate) fn to_keypair(&self) -> eyre::Result<Keypair> {
94        let keypair = match self.keypair.as_ref() {
95            Some(path) => Keypair::read_from_file(path).map_err(|err| eyre::eyre!("{err}"))?,
96            None => {
97                let mut rng = if let Some(seed) = self.seed {
98                    StdRng::seed_from_u64(seed)
99                } else {
100                    StdRng::from_entropy()
101                };
102                Keypair::generate(&mut rng)
103            }
104        };
105        Ok(keypair)
106    }
107}
108
109#[derive(Debug, clap::Args)]
110#[group(required = true, multiple = false)]
111pub(crate) struct ToggleValue {
112    #[arg(long)]
113    enable: bool,
114    #[arg(long)]
115    disable: bool,
116}
117
118impl ToggleValue {
119    pub(crate) fn is_enable(&self) -> bool {
120        debug_assert!(self.enable != self.disable);
121        self.enable
122    }
123}
124
125/// Side.
126#[derive(clap::ValueEnum, Clone, Copy, Debug)]
127pub(crate) enum Side {
128    /// Long.
129    Long,
130    /// Short.
131    Short,
132}
133
134impl Side {
135    /// Is long side.
136    pub(crate) fn is_long(&self) -> bool {
137        matches!(self, Self::Long)
138    }
139}