abstract_std/objects/entry/
dex_asset_pairing.rs1use std::fmt::Display;
2
3use cosmwasm_std::StdResult;
4use cw_storage_plus::{KeyDeserialize, Prefixer, PrimaryKey};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 constants::{ASSET_DELIMITER, TYPE_DELIMITER},
10 objects::AssetEntry,
11};
12
13type DexName = String;
14
15#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema, PartialOrd, Ord)]
18pub struct DexAssetPairing<Asset = AssetEntry>((Asset, Asset, DexName));
19
20impl<Asset> DexAssetPairing<Asset> {
21 pub fn new(asset_x: Asset, asset_y: Asset, dex_name: &str) -> Self {
22 Self((asset_x, asset_y, str::to_ascii_lowercase(dex_name)))
23 }
24
25 pub fn asset_x(&self) -> &Asset {
26 &self.0 .0
27 }
28
29 pub fn asset_y(&self) -> &Asset {
30 &self.0 .1
31 }
32
33 pub fn dex(&self) -> &str {
34 &self.0 .2
35 }
36}
37
38impl Display for DexAssetPairing {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 write!(
41 f,
42 "{}{TYPE_DELIMITER}{}{ASSET_DELIMITER}{}",
43 self.dex(),
44 self.asset_x(),
45 self.asset_y()
46 )
47 }
48}
49
50impl<'a> PrimaryKey<'a> for &DexAssetPairing {
51 type Prefix = (&'a AssetEntry, &'a AssetEntry);
52 type SubPrefix = &'a AssetEntry;
53 type Suffix = DexName;
54 type SuperSuffix = (&'a AssetEntry, DexName);
55
56 fn key(&self) -> Vec<cw_storage_plus::Key> {
57 <(AssetEntry, AssetEntry, DexName)>::key(&self.0)
58 }
59}
60
61impl Prefixer<'_> for &DexAssetPairing {
62 fn prefix(&self) -> Vec<cw_storage_plus::Key> {
63 <(AssetEntry, AssetEntry, DexName)>::prefix(&self.0)
64 }
65}
66
67impl KeyDeserialize for &DexAssetPairing {
68 type Output = DexAssetPairing;
69 const KEY_ELEMS: u16 = 3;
70
71 #[inline(always)]
72 fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
73 Ok(DexAssetPairing(
74 <(&AssetEntry, &AssetEntry, &DexName)>::from_vec(value)?,
75 ))
76 }
77}
78
79#[cfg(test)]
80mod test {
81 #![allow(clippy::needless_borrows_for_generic_args)]
82 use cosmwasm_std::{testing::mock_dependencies, Addr, Order};
83 use cw_storage_plus::Map;
84
85 use super::*;
86 use crate::objects::{AnsEntryConvertor, LpToken, PoolReference, UniquePoolId};
87
88 fn mock_key() -> DexAssetPairing {
89 DexAssetPairing::new("juno".into(), "osmo".into(), "junoswap")
90 }
91
92 fn mock_keys() -> (DexAssetPairing, DexAssetPairing, DexAssetPairing) {
93 (
94 DexAssetPairing::new("juno".into(), "osmo".into(), "junoswap"),
95 DexAssetPairing::new("juno".into(), "osmo".into(), "osmosis"),
96 DexAssetPairing::new("osmo".into(), "usdt".into(), "osmosis"),
97 )
98 }
99
100 fn mock_pool_ref(id: u64, name: &str) -> PoolReference {
101 PoolReference {
102 unique_id: UniquePoolId::new(id),
103 pool_address: Addr::unchecked(name).into(),
104 }
105 }
106
107 #[coverage_helper::test]
108 fn storage_key_works() {
109 let mut deps = mock_dependencies();
110 let key = mock_key();
111 let map: Map<&DexAssetPairing, u64> = Map::new("map");
112
113 map.save(deps.as_mut().storage, &key, &42069).unwrap();
114
115 assert_eq!(map.load(deps.as_ref().storage, &key).unwrap(), 42069);
116
117 let items = map
118 .range(deps.as_ref().storage, None, None, Order::Ascending)
119 .map(|item| item.unwrap())
120 .collect::<Vec<_>>();
121
122 assert_eq!(items.len(), 1);
123 assert_eq!(items[0], (key, 42069));
124 }
125
126 #[coverage_helper::test]
127 fn composite_key_works() {
128 let mut deps = mock_dependencies();
129 let key = mock_key();
130 let map: Map<(&DexAssetPairing, Addr), Vec<PoolReference>> = Map::new("map");
131
132 let ref_1 = mock_pool_ref(1, "larry0x");
133 let ref_2 = mock_pool_ref(2, "stablechen");
134
135 map.save(
136 deps.as_mut().storage,
137 (&key, Addr::unchecked("astroport")),
138 &vec![ref_1.clone()],
139 )
140 .unwrap();
141
142 map.save(
143 deps.as_mut().storage,
144 (&key, Addr::unchecked("terraswap")),
145 &vec![ref_2.clone()],
146 )
147 .unwrap();
148
149 let items = map
150 .prefix(&key)
151 .range(deps.as_ref().storage, None, None, Order::Ascending)
152 .map(|item| item.unwrap())
153 .collect::<Vec<_>>();
154
155 assert_eq!(items.len(), 2);
156 assert_eq!(items[0], (Addr::unchecked("astroport"), vec![ref_1]));
157 assert_eq!(items[1], (Addr::unchecked("terraswap"), vec![ref_2]));
158 }
159
160 #[coverage_helper::test]
161 fn partial_key_works() {
162 let mut deps = mock_dependencies();
163 let (key1, key2, key3) = mock_keys();
164 let map: Map<&DexAssetPairing, u64> = Map::new("map");
165
166 map.save(deps.as_mut().storage, &key1, &42069).unwrap();
167
168 map.save(deps.as_mut().storage, &key2, &69420).unwrap();
169
170 map.save(deps.as_mut().storage, &key3, &999).unwrap();
171
172 let items = map
173 .prefix((&"juno".into(), &"osmo".into()))
174 .range(deps.as_ref().storage, None, None, Order::Ascending)
175 .map(|item| item.unwrap())
176 .collect::<Vec<_>>();
177
178 assert_eq!(items.len(), 2);
179 assert_eq!(items[0], ("junoswap".to_string(), 42069));
180 assert_eq!(items[1], ("osmosis".to_string(), 69420));
181 }
182
183 #[coverage_helper::test]
184 fn try_from_lp_token() {
185 let lp = LpToken::new("junoswap", vec!["juno".to_string(), "osmo".to_string()]);
186
187 let key = AnsEntryConvertor::new(lp).dex_asset_pairing().unwrap();
188
189 assert_eq!(
190 key,
191 DexAssetPairing::new("juno".into(), "osmo".into(), "junoswap")
192 );
193 }
194
195 #[coverage_helper::test]
196 fn display() {
197 let key = DexAssetPairing::new("juno".into(), "osmo".into(), "junoswap");
198 assert_eq!(key.to_string(), "junoswap/juno,osmo");
199 }
200}