penumbra_sdk_asset/
asset.rs

1//! Asset types and identifiers.
2mod cache;
3mod denom;
4mod denom_metadata;
5mod id;
6mod r1cs;
7mod registry;
8
9pub use cache::Cache;
10pub use denom::Denom;
11pub use denom_metadata::{Metadata, Unit};
12pub use id::{Id, VALUE_GENERATOR_DOMAIN_SEP};
13pub use r1cs::AssetIdVar;
14pub use registry::{Registry, REGISTRY};
15
16// #[derive(Clone, Debug, Serialize, Deserialize)]
17// #[serde(try_from = "pb::Asset", into = "pb::Asset")]
18// pub struct Asset {
19//     pub id: Id,
20//     pub denom: DenomMetadata,
21// }
22
23// impl DomainType for Asset {
24//     type Proto = pb::Asset;
25// }
26
27// impl TryFrom<pb::Asset> for Asset {
28//     type Error = anyhow::Error;
29//     fn try_from(asset: pb::Asset) -> anyhow::Result<Self> {
30//         let denom = asset
31//             .denom
32//             .ok_or_else(|| anyhow::anyhow!("missing denom field in proto"))?
33//             .try_into()?;
34
35//         let dm = DenomMetadata::default_for(&denom)
36//             .ok_or_else(|| anyhow::anyhow!("error generating metadata for denom"))?;
37
38//         Ok(Self {
39//             id: asset
40//                 .id
41//                 .ok_or_else(|| anyhow::anyhow!("missing id field in proto"))?
42//                 .try_into()?,
43//             denom: dm,
44//         })
45//     }
46// }
47
48// impl From<Asset> for pb::Asset {
49//     fn from(asset: Asset) -> Self {
50//         Self {
51//             id: Some(asset.id.into()),
52//             denom: Some(asset.denom.base_denom().into()),
53//         }
54//     }
55// }
56
57// impl TryFrom<AssetsResponse> for Asset {
58//     type Error = anyhow::Error;
59
60//     fn try_from(response: AssetsResponse) -> Result<Self, Self::Error> {
61//         response
62//             .asset
63//             .ok_or_else(|| anyhow::anyhow!("empty AssetsResponse message"))?
64//             .try_into()
65//     }
66// }
67
68#[cfg(test)]
69mod tests {
70    use proptest::prelude::*;
71
72    use super::*;
73
74    #[test]
75    fn test_registry_native_token() {
76        // We should be able to use `parse_base` with the valid base denomination.
77        let base_denom = Cache::with_known_assets()
78            .get_unit("upenumbra")
79            .unwrap()
80            .base();
81        assert_eq!(format!("{base_denom}"), "upenumbra".to_string());
82
83        // If we try to use `parse_base` with a display denomination, we should get `None`.
84        let display_denoms = vec!["mpenumbra", "penumbra"];
85        for display_denom in &display_denoms {
86            assert!(REGISTRY.parse_denom(display_denom).is_none());
87        }
88
89        // We should be able to use the display denominations with `parse_display` however.
90        for display_denom in display_denoms {
91            let parsed_display_denom = REGISTRY.parse_unit(display_denom);
92
93            assert_eq!(
94                format!("{}", parsed_display_denom.base()),
95                "upenumbra".to_string()
96            );
97
98            assert_eq!(format!("{parsed_display_denom}"), display_denom)
99        }
100
101        // The base denomination (upenumbra) can also be used for display purposes.
102        let parsed_display_denom = REGISTRY.parse_unit("upenumbra");
103        assert_eq!(
104            format!("{}", parsed_display_denom.base()),
105            "upenumbra".to_string()
106        );
107    }
108
109    #[test]
110    fn test_displaydenom_format_value() {
111        // with exponent 6, 1782000 formats to 1.782
112        let penumbra_sdk_display_denom = REGISTRY.parse_unit("penumbra");
113        assert_eq!(
114            penumbra_sdk_display_denom.format_value(1782000u64.into()),
115            "1.782"
116        );
117        assert_eq!(
118            penumbra_sdk_display_denom.format_value(6700001u64.into()),
119            "6.700001"
120        );
121        assert_eq!(
122            penumbra_sdk_display_denom.format_value(1u64.into()),
123            "0.000001"
124        );
125
126        // with exponent 3, 1782000 formats to 1782
127        let mpenumbra_sdk_display_denom = REGISTRY.parse_unit("mpenumbra");
128        assert_eq!(
129            mpenumbra_sdk_display_denom.format_value(1782000u64.into()),
130            "1782"
131        );
132
133        // with exponent 0, 1782000 formats to 1782000
134        let upenumbra_sdk_display_denom = REGISTRY.parse_unit("upenumbra");
135        assert_eq!(
136            upenumbra_sdk_display_denom.format_value(1782000u64.into()),
137            "1782000"
138        );
139    }
140
141    #[test]
142    fn best_unit_for() {
143        let base_denom = Cache::with_known_assets()
144            .get_unit("upenumbra")
145            .unwrap()
146            .base();
147
148        assert_eq!(
149            base_denom.best_unit_for(0u64.into()).to_string(),
150            "penumbra"
151        );
152        assert_eq!(
153            base_denom.best_unit_for(999u64.into()).to_string(),
154            "upenumbra"
155        );
156        assert_eq!(
157            base_denom.best_unit_for(1_000u64.into()).to_string(),
158            "mpenumbra"
159        );
160        assert_eq!(
161            base_denom.best_unit_for(999_999u64.into()).to_string(),
162            "mpenumbra"
163        );
164        assert_eq!(
165            base_denom.best_unit_for(1_000_000u64.into()).to_string(),
166            "penumbra"
167        );
168    }
169
170    #[test]
171    fn test_displaydenom_parse_value() {
172        let penumbra_sdk_display_denom = REGISTRY.parse_unit("penumbra");
173        assert!(penumbra_sdk_display_denom.parse_value("1.2.3").is_err());
174
175        assert_eq!(
176            penumbra_sdk_display_denom.parse_value("1.782").unwrap(),
177            1782000u64.into()
178        );
179        assert_eq!(
180            penumbra_sdk_display_denom.parse_value("6.700001").unwrap(),
181            6700001u64.into()
182        );
183
184        let mpenumbra_sdk_display_denom = REGISTRY.parse_unit("mpenumbra");
185        assert_eq!(
186            mpenumbra_sdk_display_denom.parse_value("1782").unwrap(),
187            1782000u64.into()
188        );
189        assert!(mpenumbra_sdk_display_denom
190            .parse_value("1782.0001")
191            .is_err());
192
193        let upenumbra_sdk_display_denom = REGISTRY.parse_unit("upenumbra");
194        assert_eq!(
195            upenumbra_sdk_display_denom.parse_value("1782000").unwrap(),
196            1782000u64.into()
197        );
198    }
199
200    #[test]
201    fn test_get_unit() {
202        let unit = Cache::with_known_assets().get_unit("cube").unwrap();
203
204        assert_eq!(format!("{unit}"), "cube".to_string());
205    }
206
207    proptest! {
208        #[test]
209        fn displaydenom_parsing_formatting_roundtrip(
210            v: u128
211        ) {
212            let penumbra_sdk_display_denom = REGISTRY.parse_unit("penumbra");
213            let formatted = penumbra_sdk_display_denom.format_value(v.into());
214            let parsed = penumbra_sdk_display_denom.parse_value(&formatted);
215            assert_eq!(v, u128::from(parsed.unwrap()));
216
217            let mpenumbra_sdk_display_denom = REGISTRY.parse_unit("mpenumbra");
218            let formatted = mpenumbra_sdk_display_denom.format_value(v.into());
219            let parsed = mpenumbra_sdk_display_denom.parse_value(&formatted);
220            assert_eq!(v, u128::from(parsed.unwrap()));
221        }
222    }
223}