abstract_std/objects/entry/
dex_asset_pairing.rs

1use 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/// The key for an asset pairing
16/// Consists of the two assets and the dex name
17#[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}