1use std::fmt;
2
3use solana_address::Address;
4
5use super::pda::find_associated_token_pda;
6use crate::constants::TOKEN_2022_PROGRAM_ID;
7use crate::math::amm::{BuyQuote, MintFee, QuoteError, SellQuote};
8
9pub use crate::launchpad::types::TradeDirection;
12
13impl From<TradeDirection> for crate::dex::types::TradeDirection {
14 fn from(direction: TradeDirection) -> Self {
15 match direction {
16 TradeDirection::Buy => Self::Buy,
17 TradeDirection::Sell => Self::Sell,
18 }
19 }
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23pub enum TradeMode {
24 ExactIn,
25 ExactOut,
26}
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
31pub struct TradeAccounts {
32 pub user: Address,
33 pub payer: Address,
34 pub market: Address,
36 pub base_mint: Address,
37 pub quote_mint: Address,
38 pub base_vault: Address,
39 pub quote_vault: Address,
40 pub user_base_account: Address,
41 pub user_quote_account: Address,
42 pub partner: Address,
44 pub partner_config: Address,
46 pub quote_token_program: Address,
47}
48
49impl TradeAccounts {
50 #[must_use]
53 pub const fn with_stand_in_trader(self) -> StandInTrade {
54 StandInTrade(Self {
55 user: Address::new_from_array([1; 32]),
56 payer: Address::new_from_array([2; 32]),
57 user_base_account: Address::new_from_array([3; 32]),
58 user_quote_account: Address::new_from_array([4; 32]),
59 ..self
60 })
61 }
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub struct StandInTrade(TradeAccounts);
69
70impl std::ops::Deref for StandInTrade {
71 type Target = TradeAccounts;
72
73 fn deref(&self) -> &TradeAccounts {
74 &self.0
75 }
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
79pub struct QuoteRequest {
80 pub direction: TradeDirection,
81 pub mode: TradeMode,
82 pub amount: u64,
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub struct Landing {
93 pub base_fee: Option<MintFee>,
94 pub quote_fee: Option<MintFee>,
95 pub unix_timestamp: i64,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
101#[non_exhaustive]
102pub struct MarketQuote {
103 pub in_amount: u64,
105 pub out_amount: u64,
107 pub fee: u64,
109 pub fee_bps: u16,
111 pub supply_capped: bool,
115}
116
117impl MarketQuote {
118 pub(crate) const fn bought(
120 quote: &BuyQuote,
121 request: QuoteRequest,
122 fee_bps: u16,
123 ) -> Self {
124 let requested = match request.mode {
125 TradeMode::ExactIn => quote.quote_from_user,
126 TradeMode::ExactOut => quote.base_to_user,
127 };
128 Self {
129 in_amount: quote.quote_from_user,
130 out_amount: quote.base_to_user,
131 fee: quote.fee,
132 fee_bps,
133 supply_capped: requested < request.amount,
134 }
135 }
136
137 pub(crate) const fn sold(quote: &SellQuote, fee_bps: u16) -> Self {
138 Self {
139 in_amount: quote.base_from_user,
140 out_amount: quote.quote_to_user,
141 fee: quote.fee,
142 fee_bps,
143 supply_capped: false,
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150#[non_exhaustive]
151pub enum MarketQuoteError {
152 Quote(QuoteError),
153 FeeOutOfRange,
154 SupplyExhausted,
156 ExceedsQuoteHeld,
158}
159
160impl From<QuoteError> for MarketQuoteError {
161 fn from(error: QuoteError) -> Self {
162 Self::Quote(error)
163 }
164}
165
166impl fmt::Display for MarketQuoteError {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 match self {
169 Self::Quote(error) => error.fmt(f),
170 Self::FeeOutOfRange => write!(f, "fee out of range"),
171 Self::SupplyExhausted => write!(f, "the curve has no supply left"),
172 Self::ExceedsQuoteHeld => {
173 write!(f, "sell exceeds the quote the curve holds")
174 }
175 }
176 }
177}
178
179impl std::error::Error for MarketQuoteError {
180 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
181 match self {
182 Self::Quote(error) => Some(error),
183 _ => None,
184 }
185 }
186}
187
188pub(crate) struct TradeStateFields<'a> {
189 pub base_mint: &'a Address,
190 pub quote_mint: &'a Address,
191 pub platform_config: &'a Address,
193}
194
195pub(crate) struct DerivedTradeAccounts {
196 pub user_base_account: Address,
197 pub user_quote_account: Address,
198 pub partner_config: Address,
199}
200
201pub(crate) struct TradeUserContext<'a> {
202 pub user: &'a Address,
203 pub partner: &'a Address,
204 pub quote_token_program: &'a Address,
205}
206
207pub(crate) struct DeriveTradeArgs<'a> {
208 pub state: &'a TradeStateFields<'a>,
209 pub ctx: TradeUserContext<'a>,
210}
211
212pub(crate) fn derive_trade_accounts(
213 args: DeriveTradeArgs<'_>,
214) -> DerivedTradeAccounts {
215 let state = args.state;
216 let user = args.ctx.user;
217 let partner = args.ctx.partner;
218 let quote_token_program = args.ctx.quote_token_program;
219
220 let (user_base_account, _) = find_associated_token_pda(
221 user,
222 state.base_mint,
223 &TOKEN_2022_PROGRAM_ID,
224 );
225 let (user_quote_account, _) =
226 find_associated_token_pda(user, state.quote_mint, quote_token_program);
227 let (partner_config, _) = crate::nexus::pda::find_partner_config_pda(
228 state.platform_config,
229 partner,
230 );
231
232 DerivedTradeAccounts {
233 user_base_account,
234 user_quote_account,
235 partner_config,
236 }
237}