abstract_core/objects/entry/
lp_token.rs1use 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#[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 assets.sort_unstable();
31 Self {
32 dex: dex_name.to_string(),
33 assets,
34 }
35 }
36}
37
38impl 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}