phoenix/program/
token_utils.rs

1use std::{
2    fmt::Display,
3    ops::{Div, Rem},
4};
5
6use solana_program::{
7    account_info::AccountInfo,
8    entrypoint::ProgramResult,
9    program::{invoke, invoke_signed},
10    pubkey::Pubkey,
11};
12
13use crate::quantities::{BaseAtoms, QuoteAtoms, WrapperU64};
14
15use super::{checkers::TokenAccountInfo, TokenParams};
16
17#[allow(clippy::too_many_arguments)]
18pub(crate) fn try_withdraw<'a, 'info>(
19    market_key: &Pubkey,
20    base_params: &TokenParams,
21    quote_params: &TokenParams,
22    token_program: &AccountInfo<'info>,
23    quote_account: &AccountInfo<'info>,
24    quote_vault: TokenAccountInfo<'a, 'info>,
25    base_account: &AccountInfo<'info>,
26    base_vault: TokenAccountInfo<'a, 'info>,
27    quote_atoms_to_withdraw: QuoteAtoms,
28    base_atoms_to_withdraw: BaseAtoms,
29) -> ProgramResult {
30    for (withdraw_vault, withdraw_account, withdraw_amount, params) in [
31        (
32            quote_vault,
33            quote_account,
34            quote_atoms_to_withdraw.as_u64(),
35            quote_params,
36        ),
37        (
38            base_vault,
39            base_account,
40            base_atoms_to_withdraw.as_u64(),
41            base_params,
42        ),
43    ] {
44        maybe_invoke_withdraw(
45            market_key,
46            &params.mint_key,
47            params.vault_bump as u8,
48            withdraw_amount,
49            token_program,
50            withdraw_account,
51            &withdraw_vault,
52        )?;
53    }
54    Ok(())
55}
56
57pub(crate) fn maybe_invoke_withdraw<'a, 'info>(
58    market_key: &Pubkey,
59    mint_key: &Pubkey,
60    bump: u8,
61    withdraw_amount: u64,
62    token_program: &AccountInfo<'info>,
63    withdraw_account: &AccountInfo<'info>,
64    withdraw_vault: &'a TokenAccountInfo<'a, 'info>,
65) -> ProgramResult {
66    if withdraw_amount != 0 {
67        invoke_signed(
68            &spl_token::instruction::transfer(
69                token_program.key,
70                withdraw_vault.key,
71                withdraw_account.key,
72                withdraw_vault.key,
73                &[],
74                withdraw_amount,
75            )?,
76            &[
77                token_program.clone(),
78                withdraw_vault.as_ref().clone(),
79                withdraw_account.clone(),
80            ],
81            &[&[b"vault", market_key.as_ref(), mint_key.as_ref(), &[bump]]],
82        )?;
83    }
84    Ok(())
85}
86
87pub(crate) fn maybe_invoke_deposit<'a, 'info>(
88    deposit_amount: u64,
89    token_program: &AccountInfo<'info>,
90    deposit_account: &'a TokenAccountInfo<'a, 'info>,
91    deposit_vault: &'a TokenAccountInfo<'a, 'info>,
92    trader: &AccountInfo<'info>,
93) -> ProgramResult {
94    if deposit_amount > 0 {
95        invoke(
96            &spl_token::instruction::transfer(
97                token_program.key,
98                deposit_account.key,
99                deposit_vault.key,
100                trader.key,
101                &[],
102                deposit_amount,
103            )?,
104            &[
105                token_program.as_ref().clone(),
106                deposit_account.as_ref().clone(),
107                deposit_vault.as_ref().clone(),
108                trader.as_ref().clone(),
109            ],
110        )?;
111    }
112    Ok(())
113}
114
115#[allow(clippy::too_many_arguments)]
116pub(crate) fn try_deposit<'a, 'info>(
117    token_program: &AccountInfo<'info>,
118    quote_account: TokenAccountInfo<'a, 'info>,
119    quote_vault: TokenAccountInfo<'a, 'info>,
120    base_account: TokenAccountInfo<'a, 'info>,
121    base_vault: TokenAccountInfo<'a, 'info>,
122    quote_amount: QuoteAtoms,
123    base_amount: BaseAtoms,
124    trader: &AccountInfo<'info>,
125) -> ProgramResult {
126    for (deposit_vault, deposit_account, deposit_amount) in [
127        (quote_vault, quote_account, quote_amount.as_u64()),
128        (base_vault, base_account, base_amount.as_u64()),
129    ] {
130        maybe_invoke_deposit(
131            deposit_amount,
132            token_program,
133            &deposit_account,
134            &deposit_vault,
135            trader,
136        )?;
137    }
138    Ok(())
139}
140
141pub fn get_decimal_string<N: Display + Div + Rem + Copy + TryFrom<u64>>(
142    amount: N,
143    decimals: u32,
144) -> String
145where
146    <N as Rem>::Output: std::fmt::Display,
147    <N as Div>::Output: std::fmt::Display,
148    <N as TryFrom<u64>>::Error: std::fmt::Debug,
149{
150    let scale = N::try_from(10_u64.pow(decimals)).unwrap();
151    let lhs = amount / scale;
152    let rhs = format!("{:0width$}", (amount % scale), width = decimals as usize).replace('-', ""); // remove negative sign from rhs
153    format!("{}.{}", lhs, rhs.trim_end_matches('0'))
154}