1use crate::exchange::client::{accounts, args};
2use crate::exchange::events::ExchangeStats;
3use crate::exchange::types::SlippageConfig;
4use crate::util::{
5 simulation_config, ProgramClient, EXCHANGE_LOOKUP_TABLE,
6 LST_REGISTRY_LOOKUP_TABLE, SOL_USD_PYTH_FEED,
7};
8use crate::{exchange, pda, stability_pool};
9
10use std::sync::Arc;
11
12use anchor_client::solana_sdk::address_lookup_table::program::ID as LOOKUP_TABLE_PROGRAM;
13use anchor_client::solana_sdk::pubkey::Pubkey;
14use anchor_client::solana_sdk::signature::{Keypair, Signature};
15use anchor_client::Program;
16use anchor_lang::{system_program, AnchorDeserialize};
17use anchor_spl::{associated_token, token};
18use anyhow::{anyhow, Result};
19use base64::prelude::{Engine, BASE64_STANDARD};
20use fix::prelude::*;
21
22pub struct ExchangeClient {
23 program: Program<Arc<Keypair>>,
24 keypair: Arc<Keypair>,
25}
26
27impl ProgramClient for ExchangeClient {
28 const PROGRAM_ID: Pubkey = exchange::ID;
29
30 fn build_client(
31 program: Program<Arc<Keypair>>,
32 keypair: Arc<Keypair>,
33 ) -> ExchangeClient {
34 ExchangeClient { program, keypair }
35 }
36
37 fn program(&self) -> &Program<Arc<Keypair>> {
38 &self.program
39 }
40
41 fn keypair(&self) -> Arc<Keypair> {
42 self.keypair.clone()
43 }
44}
45
46impl ExchangeClient {
47 pub async fn mint_stablecoin(
52 &self,
53 amount_lst: UFix64<N9>,
54 lst_mint: Pubkey,
55 user: Pubkey,
56 slippage_config: Option<SlippageConfig>,
57 ) -> Result<Signature> {
58 let accounts = accounts::MintStablecoin {
59 user,
60 hylo: pda::hylo(),
61 fee_auth: pda::fee_auth(lst_mint),
62 vault_auth: pda::vault_auth(lst_mint),
63 stablecoin_auth: pda::hyusd_auth(),
64 fee_vault: pda::fee_vault(lst_mint),
65 lst_vault: pda::vault(lst_mint),
66 lst_header: pda::lst_header(lst_mint),
67 user_lst_ata: pda::ata(user, lst_mint),
68 user_stablecoin_ata: pda::ata(user, pda::hyusd()),
69 lst_mint,
70 stablecoin_mint: pda::hyusd(),
71 sol_usd_pyth_feed: SOL_USD_PYTH_FEED,
72 token_program: token::ID,
73 associated_token_program: associated_token::ID,
74 system_program: system_program::ID,
75 event_authority: pda::event_auth(exchange::ID),
76 program: exchange::ID,
77 };
78 let args = args::MintStablecoin {
79 amount_lst_to_deposit: amount_lst.bits,
80 slippage_config,
81 };
82 let instructions = self
83 .program
84 .request()
85 .accounts(accounts)
86 .args(args)
87 .instructions()?;
88 let lookup_tables = self
89 .load_multiple_lookup_tables(&[
90 EXCHANGE_LOOKUP_TABLE,
91 LST_REGISTRY_LOOKUP_TABLE,
92 ])
93 .await?;
94 let sig = self
95 .send_v0_transaction(&instructions, &lookup_tables)
96 .await?;
97 Ok(sig)
98 }
99
100 pub async fn redeem_stablecoin(
105 &self,
106 amount_stablecoin: UFix64<N6>,
107 lst_mint: Pubkey,
108 user: Pubkey,
109 slippage_config: Option<SlippageConfig>,
110 ) -> Result<Signature> {
111 let accounts = accounts::RedeemStablecoin {
112 user,
113 hylo: pda::hylo(),
114 fee_auth: pda::fee_auth(lst_mint),
115 vault_auth: pda::vault_auth(lst_mint),
116 stablecoin_auth: pda::hyusd_auth(),
117 fee_vault: pda::fee_vault(lst_mint),
118 lst_vault: pda::vault(lst_mint),
119 lst_header: pda::lst_header(lst_mint),
120 user_stablecoin_ata: pda::ata(user, pda::hyusd()),
121 user_lst_ata: pda::ata(user, lst_mint),
122 stablecoin_mint: pda::hyusd(),
123 lst_mint,
124 sol_usd_pyth_feed: SOL_USD_PYTH_FEED,
125 system_program: system_program::ID,
126 token_program: token::ID,
127 associated_token_program: associated_token::ID,
128 event_authority: pda::event_auth(exchange::ID),
129 program: exchange::ID,
130 };
131 let args = args::RedeemStablecoin {
132 amount_to_redeem: amount_stablecoin.bits,
133 slippage_config,
134 };
135 let instructions = self
136 .program
137 .request()
138 .accounts(accounts)
139 .args(args)
140 .instructions()?;
141 let lookup_tables = self
142 .load_multiple_lookup_tables(&[
143 EXCHANGE_LOOKUP_TABLE,
144 LST_REGISTRY_LOOKUP_TABLE,
145 ])
146 .await?;
147 let sig = self
148 .send_v0_transaction(&instructions, &lookup_tables)
149 .await?;
150 Ok(sig)
151 }
152
153 pub async fn mint_levercoin(
158 &self,
159 amount_lst: UFix64<N9>,
160 lst_mint: Pubkey,
161 user: Pubkey,
162 slippage_config: Option<SlippageConfig>,
163 ) -> Result<Signature> {
164 let accounts = accounts::MintLevercoin {
165 user,
166 hylo: pda::hylo(),
167 fee_auth: pda::fee_auth(lst_mint),
168 vault_auth: pda::vault_auth(lst_mint),
169 levercoin_auth: pda::xsol_auth(),
170 fee_vault: pda::fee_vault(lst_mint),
171 lst_vault: pda::vault(lst_mint),
172 lst_header: pda::lst_header(lst_mint),
173 user_lst_ata: pda::ata(user, lst_mint),
174 user_levercoin_ata: pda::ata(user, pda::xsol()),
175 lst_mint,
176 levercoin_mint: pda::xsol(),
177 stablecoin_mint: pda::hyusd(),
178 sol_usd_pyth_feed: SOL_USD_PYTH_FEED,
179 token_program: token::ID,
180 associated_token_program: associated_token::ID,
181 system_program: system_program::ID,
182 event_authority: pda::event_auth(exchange::ID),
183 program: exchange::ID,
184 };
185 let args = args::MintLevercoin {
186 amount_lst_to_deposit: amount_lst.bits,
187 slippage_config,
188 };
189 let instructions = self
190 .program
191 .request()
192 .accounts(accounts)
193 .args(args)
194 .instructions()?;
195 let lookup_tables = self
196 .load_multiple_lookup_tables(&[
197 EXCHANGE_LOOKUP_TABLE,
198 LST_REGISTRY_LOOKUP_TABLE,
199 ])
200 .await?;
201 let sig = self
202 .send_v0_transaction(&instructions, &lookup_tables)
203 .await?;
204 Ok(sig)
205 }
206
207 pub async fn redeem_levercoin(
212 &self,
213 amount_levercoin: UFix64<N6>,
214 lst_mint: Pubkey,
215 user: Pubkey,
216 slippage_config: Option<SlippageConfig>,
217 ) -> Result<Signature> {
218 let accounts = accounts::RedeemLevercoin {
219 user,
220 hylo: pda::hylo(),
221 fee_auth: pda::fee_auth(lst_mint),
222 vault_auth: pda::vault_auth(lst_mint),
223 levercoin_auth: pda::xsol_auth(),
224 fee_vault: pda::fee_vault(lst_mint),
225 lst_vault: pda::vault(lst_mint),
226 lst_header: pda::lst_header(lst_mint),
227 user_levercoin_ata: pda::ata(user, pda::xsol()),
228 user_lst_ata: pda::ata(user, lst_mint),
229 levercoin_mint: pda::xsol(),
230 stablecoin_mint: pda::hyusd(),
231 lst_mint,
232 sol_usd_pyth_feed: SOL_USD_PYTH_FEED,
233 system_program: system_program::ID,
234 token_program: token::ID,
235 associated_token_program: associated_token::ID,
236 event_authority: pda::event_auth(exchange::ID),
237 program: exchange::ID,
238 };
239 let args = args::RedeemLevercoin {
240 amount_to_redeem: amount_levercoin.bits,
241 slippage_config,
242 };
243 let instructions = self
244 .program
245 .request()
246 .accounts(accounts)
247 .args(args)
248 .instructions()?;
249 let lookup_tables = self
250 .load_multiple_lookup_tables(&[
251 EXCHANGE_LOOKUP_TABLE,
252 LST_REGISTRY_LOOKUP_TABLE,
253 ])
254 .await?;
255 let sig = self
256 .send_v0_transaction(&instructions, &lookup_tables)
257 .await?;
258 Ok(sig)
259 }
260
261 pub async fn update_lst_prices(&self) -> Result<Signature> {
266 let accounts = accounts::UpdateLstPrices {
267 payer: self.program.payer(),
268 hylo: pda::hylo(),
269 lst_registry: LST_REGISTRY_LOOKUP_TABLE,
270 lut_program: LOOKUP_TABLE_PROGRAM,
271 event_authority: pda::event_auth(exchange::ID),
272 program: exchange::ID,
273 };
274 let args = args::UpdateLstPrices {};
275 let (remaining_accounts, registry_lut) = self.load_lst_registry().await?;
276 let instructions = self
277 .program
278 .request()
279 .accounts(accounts)
280 .accounts(remaining_accounts)
281 .args(args)
282 .instructions()?;
283 let exchange_lut = self.load_lookup_table(&EXCHANGE_LOOKUP_TABLE).await?;
284 let sig = self
285 .send_v0_transaction(&instructions, &[registry_lut, exchange_lut])
286 .await?;
287 Ok(sig)
288 }
289
290 pub async fn harvest_yield(&self) -> Result<Signature> {
295 let accounts = accounts::HarvestYield {
296 payer: self.program.payer(),
297 hylo: pda::hylo(),
298 stablecoin_mint: pda::hyusd(),
299 stablecoin_auth: pda::hyusd_auth(),
300 levercoin_mint: pda::xsol(),
301 levercoin_auth: pda::xsol_auth(),
302 fee_auth: pda::fee_auth(pda::hyusd()),
303 fee_vault: pda::fee_vault(pda::hyusd()),
304 stablecoin_pool: pda::hyusd_pool(),
305 levercoin_pool: pda::xsol_pool(),
306 pool_auth: pda::pool_auth(),
307 sol_usd_pyth_feed: SOL_USD_PYTH_FEED,
308 hylo_stability_pool: stability_pool::ID,
309 lst_registry: LST_REGISTRY_LOOKUP_TABLE,
310 lut_program: LOOKUP_TABLE_PROGRAM,
311 associated_token_program: associated_token::ID,
312 token_program: token::ID,
313 system_program: system_program::ID,
314 event_authority: pda::event_auth(exchange::ID),
315 program: exchange::ID,
316 };
317 let args = args::HarvestYield {};
318 let (remaining_accounts, registry_lut) = self.load_lst_registry().await?;
319 let instructions = self
320 .program
321 .request()
322 .accounts(accounts)
323 .accounts(remaining_accounts)
324 .args(args)
325 .instructions()?;
326 let exchange_lut = self.load_lookup_table(&EXCHANGE_LOOKUP_TABLE).await?;
327 let sig = self
328 .send_v0_transaction(&instructions, &[registry_lut, exchange_lut])
329 .await?;
330 Ok(sig)
331 }
332
333 pub async fn simulate_get_stats(&self) -> Result<ExchangeStats> {
339 let accounts = accounts::GetStats {
340 hylo: pda::hylo(),
341 stablecoin_mint: pda::hyusd(),
342 levercoin_mint: pda::xsol(),
343 sol_usd_pyth_feed: SOL_USD_PYTH_FEED,
344 };
345 let args = args::GetStats {};
346 let tx = self
347 .program
348 .request()
349 .accounts(accounts)
350 .args(args)
351 .signed_transaction()
352 .await?;
353 let rpc = self.program.rpc();
354 let result = rpc
355 .simulate_transaction_with_config(&tx, simulation_config())
356 .await?;
357 let (data, _) = result
358 .value
359 .return_data
360 .ok_or(anyhow!("No return data for `get_stats`"))?
361 .data;
362 let bytes = BASE64_STANDARD.decode(data)?;
363 let stats = ExchangeStats::try_from_slice(&bytes)?;
364 Ok(stats)
365 }
366}