1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use sp_core::bounded_vec::BoundedVec;
use serai_abi::primitives::{Amount, Coin, ExternalCoin, SeraiAddress};

use crate::{SeraiError, TemporalSerai};

pub type DexEvent = serai_abi::dex::Event;

const PALLET: &str = "Dex";

#[derive(Clone, Copy)]
pub struct SeraiDex<'a>(pub(crate) &'a TemporalSerai<'a>);
impl<'a> SeraiDex<'a> {
  pub async fn events(&self) -> Result<Vec<DexEvent>, SeraiError> {
    self
      .0
      .events(
        |event| if let serai_abi::Event::Dex(event) = event { Some(event.clone()) } else { None },
      )
      .await
  }

  pub fn add_liquidity(
    coin: ExternalCoin,
    coin_amount: Amount,
    sri_amount: Amount,
    min_coin_amount: Amount,
    min_sri_amount: Amount,
    address: SeraiAddress,
  ) -> serai_abi::Call {
    serai_abi::Call::Dex(serai_abi::dex::Call::add_liquidity {
      coin,
      coin_desired: coin_amount.0,
      sri_desired: sri_amount.0,
      coin_min: min_coin_amount.0,
      sri_min: min_sri_amount.0,
      mint_to: address,
    })
  }

  pub fn swap(
    from_coin: Coin,
    to_coin: Coin,
    amount_in: Amount,
    amount_out_min: Amount,
    address: SeraiAddress,
  ) -> serai_abi::Call {
    let path = if to_coin.is_native() {
      BoundedVec::try_from(vec![from_coin, Coin::Serai]).unwrap()
    } else if from_coin.is_native() {
      BoundedVec::try_from(vec![Coin::Serai, to_coin]).unwrap()
    } else {
      BoundedVec::try_from(vec![from_coin, Coin::Serai, to_coin]).unwrap()
    };

    serai_abi::Call::Dex(serai_abi::dex::Call::swap_exact_tokens_for_tokens {
      path,
      amount_in: amount_in.0,
      amount_out_min: amount_out_min.0,
      send_to: address,
    })
  }

  /// Returns the reserves of `coin:SRI` pool.
  pub async fn get_reserves(
    &self,
    coin: ExternalCoin,
  ) -> Result<Option<(Amount, Amount)>, SeraiError> {
    self.0.runtime_api("DexApi_get_reserves", (Coin::from(coin), Coin::Serai)).await
  }

  pub async fn oracle_value(&self, coin: ExternalCoin) -> Result<Option<Amount>, SeraiError> {
    self.0.storage(PALLET, "SecurityOracleValue", coin).await
  }
}