gmsol_cli/commands/
utils.rs1use 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
56pub(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 keypair: Option<std::path::PathBuf>,
87 #[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#[derive(clap::ValueEnum, Clone, Copy, Debug)]
127pub(crate) enum Side {
128 Long,
130 Short,
132}
133
134impl Side {
135 pub(crate) fn is_long(&self) -> bool {
137 matches!(self, Self::Long)
138 }
139}