abstract_core/objects/entry/
lp_token.rs

1use std::fmt::Display;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    constants::{ASSET_DELIMITER, TYPE_DELIMITER},
8    objects::AssetEntry,
9};
10
11pub type DexName = String;
12
13/// A key for the token that represents Liquidity Pool shares on a dex
14/// Will be formatted as "dex_name/asset1,asset2" when serialized
15#[derive(
16    Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema, PartialOrd, Ord, Default,
17)]
18pub struct LpToken {
19    pub dex: DexName,
20    pub assets: Vec<AssetEntry>,
21}
22
23impl LpToken {
24    pub fn new<T: ToString, U: Into<AssetEntry> + Clone>(dex_name: T, assets: Vec<U>) -> Self {
25        let mut assets = assets
26            .into_iter()
27            .map(|a| a.into())
28            .collect::<Vec<AssetEntry>>();
29        // sort the asset name
30        assets.sort_unstable();
31        Self {
32            dex: dex_name.to_string(),
33            assets,
34        }
35    }
36}
37
38/// Transform into a string formatted as "dex_name/asset1,asset2"
39impl Display for LpToken {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        let assets = self
42            .assets
43            .iter()
44            .map(|a| a.as_str())
45            .collect::<Vec<&str>>()
46            .join(ASSET_DELIMITER);
47
48        write!(f, "{}{}{}", self.dex, TYPE_DELIMITER, assets)
49    }
50}
51
52#[cfg(test)]
53mod test {
54    use speculoos::prelude::*;
55
56    use super::*;
57
58    mod implementation {
59        use super::*;
60
61        #[test]
62        fn new_works() {
63            let dex_name = "junoswap";
64            let mut assets = vec![AssetEntry::from("junox"), AssetEntry::from("crab")];
65            let actual = LpToken::new(dex_name, assets.clone());
66            assets.sort();
67            let expected = LpToken {
68                dex: dex_name.to_string(),
69                assets,
70            };
71            assert_that!(actual).is_equal_to(expected);
72        }
73
74        #[test]
75        fn assets_returns_asset_entries() {
76            let dex_name = "junoswap";
77            let assets = vec![AssetEntry::from("crab"), AssetEntry::from("junox")];
78            let lp_token = LpToken::new(dex_name, assets);
79            let expected = vec![AssetEntry::from("crab"), AssetEntry::from("junox")];
80
81            assert_that!(lp_token.assets).is_equal_to(expected);
82        }
83    }
84
85    mod from_asset_entry {
86        use super::*;
87        use crate::objects::AnsEntryConvertor;
88
89        #[test]
90        fn test_from_asset_entry() {
91            let asset = AssetEntry::new("junoswap/crab,junox");
92            let lp_token = AnsEntryConvertor::new(asset).lp_token().unwrap();
93            assert_that!(lp_token.dex).is_equal_to("junoswap".to_string());
94            assert_that!(lp_token.assets)
95                .is_equal_to(vec![AssetEntry::from("crab"), AssetEntry::from("junox")]);
96        }
97
98        #[test]
99        fn test_from_invalid_asset_entry() {
100            let asset = AssetEntry::new("junoswap/");
101            let lp_token = AnsEntryConvertor::new(asset).lp_token();
102            assert_that!(&lp_token).is_err();
103        }
104
105        #[test]
106        fn test_fewer_than_two_assets() {
107            let asset = AssetEntry::new("junoswap/crab");
108            let lp_token = AnsEntryConvertor::new(asset).lp_token();
109            assert_that!(&lp_token).is_err();
110        }
111    }
112
113    mod into_asset_entry {
114        use super::*;
115        use crate::objects::AnsEntryConvertor;
116
117        #[test]
118        fn into_asset_entry_works() {
119            let lp_token = LpToken::new("junoswap", vec!["crab".to_string(), "junox".to_string()]);
120            let expected = AssetEntry::new("junoswap/crab,junox");
121
122            assert_that!(AnsEntryConvertor::new(lp_token).asset_entry()).is_equal_to(expected);
123        }
124    }
125
126    mod from_pool_metadata {
127        use super::*;
128        use crate::objects::{AnsEntryConvertor, PoolMetadata, PoolType};
129
130        #[test]
131        fn test_from_pool_metadata() {
132            let assets: Vec<AssetEntry> = vec!["crab".into(), "junox".into()];
133            let dex = "junoswap".to_string();
134
135            let pool = PoolMetadata {
136                dex: dex.clone(),
137                pool_type: PoolType::Stable,
138                assets: assets.clone(),
139            };
140
141            let lp_token = AnsEntryConvertor::new(pool).lp_token();
142            assert_that!(lp_token.dex).is_equal_to(dex);
143            assert_that!(lp_token.assets).is_equal_to(assets);
144        }
145    }
146}