abstract_core/objects/pool/
pool_metadata.rs1use std::{fmt, str::FromStr};
2
3use cosmwasm_std::StdError;
4use cw_asset::AssetInfo;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 constants::{ASSET_DELIMITER, ATTRIBUTE_DELIMITER, TYPE_DELIMITER},
10 objects::{pool_type::PoolType, AssetEntry},
11};
12
13type DexName = String;
14
15#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
16pub struct PoolMetadata {
17 pub dex: DexName,
18 pub pool_type: PoolType,
19 pub assets: Vec<AssetEntry>,
20}
21
22#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
23pub struct ResolvedPoolMetadata {
24 pub dex: DexName,
25 pub pool_type: PoolType,
26 pub assets: Vec<AssetInfo>,
27}
28
29impl PoolMetadata {
30 pub fn new<T: ToString, U: Into<AssetEntry>>(
31 dex_name: T,
32 pool_type: PoolType,
33 assets: Vec<U>,
34 ) -> Self {
35 let mut assets = assets
36 .into_iter()
37 .map(|a| a.into())
38 .collect::<Vec<AssetEntry>>();
39 assets.sort_unstable();
41 Self {
42 dex: dex_name.to_string(),
43 pool_type,
44 assets,
45 }
46 }
47
48 pub fn stable<T: ToString>(dex_name: T, assets: Vec<impl Into<AssetEntry>>) -> Self {
49 Self::new(dex_name, PoolType::Stable, assets)
50 }
51
52 pub fn weighted<T: ToString>(dex_name: T, assets: Vec<impl Into<AssetEntry>>) -> Self {
53 Self::new(dex_name, PoolType::Weighted, assets)
54 }
55
56 pub fn constant_product<T: ToString>(dex_name: T, assets: Vec<impl Into<AssetEntry>>) -> Self {
57 Self::new(dex_name, PoolType::ConstantProduct, assets)
58 }
59
60 pub fn liquidity_bootstrap<T: ToString>(
61 dex_name: T,
62 assets: Vec<impl Into<AssetEntry>>,
63 ) -> Self {
64 Self::new(dex_name, PoolType::LiquidityBootstrap, assets)
65 }
66
67 pub fn concentrated_liquidity<T: ToString>(
68 dex_name: T,
69 assets: Vec<impl Into<AssetEntry>>,
70 ) -> Self {
71 Self::new(dex_name, PoolType::ConcentratedLiquidity, assets)
72 }
73}
74
75impl FromStr for PoolMetadata {
76 type Err = StdError;
77
78 fn from_str(s: &str) -> Result<Self, Self::Err> {
79 let parts = s.split_once(TYPE_DELIMITER).and_then(|(dex, remainder)| {
81 remainder
82 .split_once(ATTRIBUTE_DELIMITER)
83 .map(|(assets, pool_type)| (dex, assets, pool_type))
84 });
85 let Some((dex, assets, pool_type)) = parts else {
86 return Err(StdError::generic_err(format!(
87 "invalid pool metadata format `{s}`; must be in format `{{dex}}{TYPE_DELIMITER}{{asset1}},{{asset2}}{ATTRIBUTE_DELIMITER}{{pool_type}}...`"
88 )));
89 };
90
91 let assets: Vec<&str> = assets.split(ASSET_DELIMITER).collect();
92 let pool_type = PoolType::from_str(pool_type)?;
93
94 Ok(PoolMetadata::new(dex, pool_type, assets))
95 }
96}
97
98impl fmt::Display for PoolMetadata {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 let assets_str = self
103 .assets
104 .iter()
105 .map(|a| a.as_str())
106 .collect::<Vec<&str>>()
107 .join(ASSET_DELIMITER);
108 let pool_type_str = self.pool_type.to_string();
109 let dex = &self.dex;
110
111 write!(
112 f,
113 "{dex}{TYPE_DELIMITER}{assets_str}{ATTRIBUTE_DELIMITER}{pool_type_str}",
114 )
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use speculoos::prelude::*;
121
122 use super::*;
123
124 mod implementation {
125 use super::*;
126
127 #[test]
128 fn new_works() {
129 let dex = "junoswap";
130 let pool_type = PoolType::Stable;
131 let mut assets = vec!["uust".to_string(), "uusd".to_string()];
132 let actual = PoolMetadata::new(dex, pool_type, assets.clone());
133 assets.sort();
135 let expected = PoolMetadata {
136 dex: dex.to_string(),
137 pool_type,
138 assets: assets.into_iter().map(|a| a.into()).collect(),
139 };
140 assert_that!(actual).is_equal_to(expected);
141 assert_that!(actual.to_string()).is_equal_to("junoswap/uusd,uust:stable".to_string());
142 }
143
144 #[test]
145 fn stable_works() {
146 let dex = "junoswap";
147 let assets = vec!["uusd".to_string(), "uust".to_string()];
148 let actual = PoolMetadata::stable(dex, assets.clone());
149
150 let expected = PoolMetadata {
151 dex: dex.to_string(),
152 pool_type: PoolType::Stable,
153 assets: assets.into_iter().map(|a| a.into()).collect(),
154 };
155 assert_that!(actual).is_equal_to(expected);
156 }
157
158 #[test]
159 fn weighted_works() {
160 let dex = "junoswap";
161 let assets = vec!["uusd".to_string(), "uust".to_string()];
162 let actual = PoolMetadata::weighted(dex, assets.clone());
163
164 let expected = PoolMetadata {
165 dex: dex.to_string(),
166 pool_type: PoolType::Weighted,
167 assets: assets.into_iter().map(|a| a.into()).collect(),
168 };
169 assert_that!(actual).is_equal_to(expected);
170 }
171
172 #[test]
173 fn constant_product_works() {
174 let dex = "junoswap";
175 let assets = vec!["uusd".to_string(), "uust".to_string()];
176 let actual = PoolMetadata::constant_product(dex, assets.clone());
177
178 let expected = PoolMetadata {
179 dex: dex.to_string(),
180 pool_type: PoolType::ConstantProduct,
181 assets: assets.into_iter().map(|a| a.into()).collect(),
182 };
183 assert_that!(actual).is_equal_to(expected);
184 }
185
186 #[test]
187 fn liquidity_bootstrap_works() {
188 let dex = "junoswap";
189 let assets = vec!["uusd".to_string(), "uust".to_string()];
190 let actual = PoolMetadata::liquidity_bootstrap(dex, assets.clone());
191
192 let expected = PoolMetadata {
193 dex: dex.to_string(),
194 pool_type: PoolType::LiquidityBootstrap,
195 assets: assets.into_iter().map(|a| a.into()).collect(),
196 };
197 assert_that!(actual).is_equal_to(expected);
198 }
199 }
200
201 #[test]
202 fn test_pool_metadata_from_str() {
203 let pool_metadata_str = "junoswap/uusd,uust:stable";
204 let pool_metadata = PoolMetadata::from_str(pool_metadata_str).unwrap();
205
206 assert_eq!(pool_metadata.dex, "junoswap");
207 assert_eq!(
208 pool_metadata.assets,
209 vec!["uusd", "uust"]
210 .into_iter()
211 .map(AssetEntry::from)
212 .collect::<Vec<AssetEntry>>()
213 );
214 assert_eq!(pool_metadata.pool_type, PoolType::Stable);
215
216 let pool_metadata_str = "junoswap:uusd,uust/stable";
218 let err = PoolMetadata::from_str(pool_metadata_str).unwrap_err();
219
220 assert_eq!(err, StdError::generic_err(format!(
221 "invalid pool metadata format `{pool_metadata_str}`; must be in format `{{dex}}{TYPE_DELIMITER}{{asset1}},{{asset2}}{ATTRIBUTE_DELIMITER}{{pool_type}}...`"
222 )));
223 }
224
225 #[test]
226 fn test_pool_metadata_to_string() {
227 let pool_metadata_str = "junoswap/uusd,uust:weighted";
228 let pool_metadata = PoolMetadata::from_str(pool_metadata_str).unwrap();
229
230 assert_eq!(pool_metadata.to_string(), pool_metadata_str);
231 }
232}