abstract_std/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
121 use super::*;
122
123 mod implementation {
124 use super::*;
125
126 #[coverage_helper::test]
127 fn new_works() {
128 let dex = "junoswap";
129 let pool_type = PoolType::Stable;
130 let mut assets = vec!["uust".to_string(), "uusd".to_string()];
131 let actual = PoolMetadata::new(dex, pool_type, assets.clone());
132 assets.sort();
134 let expected = PoolMetadata {
135 dex: dex.to_string(),
136 pool_type,
137 assets: assets.into_iter().map(|a| a.into()).collect(),
138 };
139 assert_eq!(actual, expected);
140 assert_eq!(actual.to_string(), "junoswap/uusd,uust:stable".to_string());
141 }
142
143 #[coverage_helper::test]
144 fn stable_works() {
145 let dex = "junoswap";
146 let assets = vec!["uusd".to_string(), "uust".to_string()];
147 let actual = PoolMetadata::stable(dex, assets.clone());
148
149 let expected = PoolMetadata {
150 dex: dex.to_string(),
151 pool_type: PoolType::Stable,
152 assets: assets.into_iter().map(|a| a.into()).collect(),
153 };
154 assert_eq!(actual, expected);
155 }
156
157 #[coverage_helper::test]
158 fn weighted_works() {
159 let dex = "junoswap";
160 let assets = vec!["uusd".to_string(), "uust".to_string()];
161 let actual = PoolMetadata::weighted(dex, assets.clone());
162
163 let expected = PoolMetadata {
164 dex: dex.to_string(),
165 pool_type: PoolType::Weighted,
166 assets: assets.into_iter().map(|a| a.into()).collect(),
167 };
168 assert_eq!(actual, expected);
169 }
170
171 #[coverage_helper::test]
172 fn constant_product_works() {
173 let dex = "junoswap";
174 let assets = vec!["uusd".to_string(), "uust".to_string()];
175 let actual = PoolMetadata::constant_product(dex, assets.clone());
176
177 let expected = PoolMetadata {
178 dex: dex.to_string(),
179 pool_type: PoolType::ConstantProduct,
180 assets: assets.into_iter().map(|a| a.into()).collect(),
181 };
182 assert_eq!(actual, expected);
183 }
184
185 #[coverage_helper::test]
186 fn liquidity_bootstrap_works() {
187 let dex = "junoswap";
188 let assets = vec!["uusd".to_string(), "uust".to_string()];
189 let actual = PoolMetadata::liquidity_bootstrap(dex, assets.clone());
190
191 let expected = PoolMetadata {
192 dex: dex.to_string(),
193 pool_type: PoolType::LiquidityBootstrap,
194 assets: assets.into_iter().map(|a| a.into()).collect(),
195 };
196 assert_eq!(actual, expected);
197 }
198 }
199
200 #[coverage_helper::test]
201 fn test_pool_metadata_from_str() {
202 let pool_metadata_str = "junoswap/uusd,uust:stable";
203 let pool_metadata = PoolMetadata::from_str(pool_metadata_str).unwrap();
204
205 assert_eq!(pool_metadata.dex, "junoswap");
206 assert_eq!(
207 pool_metadata.assets,
208 vec!["uusd", "uust"]
209 .into_iter()
210 .map(AssetEntry::from)
211 .collect::<Vec<AssetEntry>>()
212 );
213 assert_eq!(pool_metadata.pool_type, PoolType::Stable);
214
215 let pool_metadata_str = "junoswap:uusd,uust/stable";
217 let err = PoolMetadata::from_str(pool_metadata_str).unwrap_err();
218
219 assert_eq!(err, StdError::generic_err(format!(
220 "invalid pool metadata format `{pool_metadata_str}`; must be in format `{{dex}}{TYPE_DELIMITER}{{asset1}},{{asset2}}{ATTRIBUTE_DELIMITER}{{pool_type}}...`"
221 )));
222 }
223
224 #[coverage_helper::test]
225 fn test_pool_metadata_to_string() {
226 let pool_metadata_str = "junoswap/uusd,uust:weighted";
227 let pool_metadata = PoolMetadata::from_str(pool_metadata_str).unwrap();
228
229 assert_eq!(pool_metadata.to_string(), pool_metadata_str);
230 }
231}