Skip to main content

hylo_sdk/
exchange_client.rs

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  /// Mints stablecoin against the given LST.
48  ///
49  /// # Errors
50  /// - Transaction failure
51  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  /// Redeems stablecoin into the given LST.
101  ///
102  /// # Errors
103  /// - Transaction failure
104  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  /// Mints levercoin against the given LST.
154  ///
155  /// # Errors
156  /// - Transaction failure
157  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  /// Redeems levercoin into the given LST.
208  ///
209  /// # Errors
210  /// - Transaction failure
211  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  /// Runs exchange's LST price oracle crank.
262  ///
263  /// # Errors
264  /// - Transaction failure
265  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  /// Harvests yield from LST vaults to stability pool.
291  ///
292  /// # Errors
293  /// - Transaction failure
294  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  /// Simulates the `get_stats` instruction on the exchange.
334  ///
335  /// # Errors
336  /// - Simulation failure
337  /// - Return data access or deserialization
338  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}