Skip to main content

gmsol_sdk/builders/
utils.rs

1use anchor_spl::associated_token::get_associated_token_address_with_program_id;
2use gmsol_solana_utils::client_traits::FromRpcClientWith;
3use solana_sdk::{instruction::Instruction, pubkey::Pubkey};
4use typed_builder::TypedBuilder;
5
6use crate::{
7    builders::{market::MarketTokenIxBuilder, store_program::StoreProgramIxBuilder},
8    serde::StringPubkey,
9};
10
11use super::NonceBytes;
12
13pub(crate) fn generate_nonce() -> NonceBytes {
14    use rand::{distributions::Standard, Rng};
15
16    let pubkey = rand::thread_rng()
17        .sample_iter(Standard)
18        .take(32)
19        .collect::<Vec<u8>>()
20        .try_into()
21        .unwrap();
22    StringPubkey(pubkey)
23}
24
25pub(crate) fn prepare_ata(
26    payer: &Pubkey,
27    owner: &Pubkey,
28    token: Option<&Pubkey>,
29    token_program_id: &Pubkey,
30) -> Option<(Pubkey, Instruction)> {
31    use anchor_spl::associated_token::spl_associated_token_account::instruction;
32
33    let token = token?;
34
35    let ata = get_associated_token_address_with_program_id(owner, token, token_program_id);
36
37    let prepare = instruction::create_associated_token_account_idempotent(
38        payer,
39        owner,
40        token,
41        token_program_id,
42    );
43
44    Some((ata, prepare))
45}
46
47pub(crate) fn get_ata_or_owner(
48    owner: &Pubkey,
49    mint: &Pubkey,
50    should_unwrap_native_token: bool,
51) -> Pubkey {
52    get_ata_or_owner_with_program_id(
53        owner,
54        mint,
55        should_unwrap_native_token,
56        &anchor_spl::token::ID,
57    )
58}
59
60pub(crate) fn get_ata_or_owner_with_program_id(
61    owner: &Pubkey,
62    mint: &Pubkey,
63    should_unwrap_native_token: bool,
64    token_program_id: &Pubkey,
65) -> Pubkey {
66    use anchor_spl::{
67        associated_token::get_associated_token_address_with_program_id,
68        token::spl_token::native_mint,
69    };
70
71    if should_unwrap_native_token && *mint == native_mint::ID {
72        *owner
73    } else {
74        get_associated_token_address_with_program_id(owner, mint, token_program_id)
75    }
76}
77
78/// Hint for pool tokens.
79#[cfg_attr(js, derive(tsify_next::Tsify))]
80#[cfg_attr(js, tsify(from_wasm_abi))]
81#[cfg_attr(serde, derive(serde::Serialize, serde::Deserialize))]
82#[derive(Debug, Clone, TypedBuilder)]
83pub struct PoolTokenHint {
84    /// Long token.
85    #[builder(setter(into))]
86    pub long_token: StringPubkey,
87    /// Short token.
88    #[builder(setter(into))]
89    pub short_token: StringPubkey,
90}
91
92impl PoolTokenHint {
93    /// Returns whether the given token is long token or short token.
94    /// # Errors
95    /// - Returns Error if the given `collateral` is not one of the specified long or short tokens.
96    pub fn is_collateral_long(&self, collateral: &Pubkey) -> Result<bool, crate::SolanaUtilsError> {
97        if *collateral == *self.long_token {
98            Ok(true)
99        } else if *collateral == *self.short_token {
100            Ok(false)
101        } else {
102            Err(crate::SolanaUtilsError::custom(
103                "invalid hint: `collateral` is not one of the specified long or short tokens",
104            ))
105        }
106    }
107}
108
109impl<T> FromRpcClientWith<T> for PoolTokenHint
110where
111    T: StoreProgramIxBuilder + MarketTokenIxBuilder,
112{
113    async fn from_rpc_client_with<'a>(
114        builder: &'a T,
115        client: &'a impl gmsol_solana_utils::client_traits::RpcClient,
116    ) -> gmsol_solana_utils::Result<Self> {
117        use crate::{programs::gmsol_store::accounts::Market, utils::zero_copy::ZeroCopy};
118        use gmsol_solana_utils::client_traits::RpcClientExt;
119
120        let market_address = builder
121            .store_program()
122            .find_market_address(builder.market_token());
123        let market = client
124            .get_anchor_account::<ZeroCopy<Market>>(&market_address, Default::default())
125            .await?
126            .0;
127
128        Ok(Self {
129            long_token: market.meta.long_token_mint.into(),
130            short_token: market.meta.short_token_mint.into(),
131        })
132    }
133}