abstract_std/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 #![allow(clippy::needless_borrows_for_generic_args)]
55
56 use super::*;
57
58 mod implementation {
59 use super::*;
60
61 #[coverage_helper::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_eq!(actual, expected);
72 }
73
74 #[coverage_helper::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_eq!(lp_token.assets, expected);
82 }
83 }
84
85 mod from_asset_entry {
86 use super::*;
87 use crate::objects::AnsEntryConvertor;
88
89 #[coverage_helper::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_eq!(lp_token.dex, "junoswap".to_string());
94 assert_eq!(
95 lp_token.assets,
96 vec![AssetEntry::from("crab"), AssetEntry::from("junox")]
97 );
98 }
99
100 #[coverage_helper::test]
101 fn test_from_invalid_asset_entry() {
102 let asset = AssetEntry::new("junoswap/");
103 let lp_token = AnsEntryConvertor::new(asset).lp_token();
104 assert!(lp_token.is_err());
105 }
106
107 #[coverage_helper::test]
108 fn test_fewer_than_two_assets() {
109 let asset = AssetEntry::new("junoswap/crab");
110 let lp_token = AnsEntryConvertor::new(asset).lp_token();
111 assert!(lp_token.is_err());
112 }
113 }
114
115 mod into_asset_entry {
116 use super::*;
117 use crate::objects::AnsEntryConvertor;
118
119 #[coverage_helper::test]
120 fn into_asset_entry_works() {
121 let lp_token = LpToken::new("junoswap", vec!["crab".to_string(), "junox".to_string()]);
122 let expected = AssetEntry::new("junoswap/crab,junox");
123
124 assert_eq!(AnsEntryConvertor::new(lp_token).asset_entry(), expected);
125 }
126 }
127
128 mod from_pool_metadata {
129 use super::*;
130 use crate::objects::{AnsEntryConvertor, PoolMetadata, PoolType};
131
132 #[coverage_helper::test]
133 fn test_from_pool_metadata() {
134 let assets: Vec<AssetEntry> = vec!["crab".into(), "junox".into()];
135 let dex = "junoswap".to_string();
136
137 let pool = PoolMetadata {
138 dex: dex.clone(),
139 pool_type: PoolType::Stable,
140 assets: assets.clone(),
141 };
142
143 let lp_token = AnsEntryConvertor::new(pool).lp_token();
144 assert_eq!(lp_token.dex, dex);
145 assert_eq!(lp_token.assets, assets);
146 }
147 }
148}