1use schemars::JsonSchema;
2use serde::{Deserialize, Deserializer, Serialize};
3
4use crate::prelude::*;
5
6#[derive(
8 Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
9)]
10#[schemaifier(mute_warnings)] pub struct DenomMetadata {
12 pub description: String,
13 #[serde(deserialize_with = "deserialize_null_default")]
14 pub denom_units: Vec<DenomUnit>,
15 pub base: String,
16 pub display: String,
17 pub name: String,
18 pub symbol: String,
19 pub uri: String,
20 pub uri_hash: String,
21}
22
23#[derive(
25 Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
26)]
27#[schemaifier(mute_warnings)] pub struct DenomUnit {
29 pub denom: String,
30 pub exponent: u32,
31 #[serde(deserialize_with = "deserialize_null_default")]
32 pub aliases: Vec<String>,
33}
34
35fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
38where
39 T: Default + Deserialize<'de>,
40 D: Deserializer<'de>,
41{
42 let opt = Option::deserialize(deserializer)?;
43 Ok(opt.unwrap_or_default())
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::{DenomMetadata, DenomUnit};
50 use serde_json::{json, Error};
51
52 #[test]
53 fn deserialize_denom_metadata_with_null_fields_works() {
54 let json_with_null_denom_units = json!({
56 "description": "Test Token",
57 "denom_units": null,
58 "base": "utest",
59 "display": "TEST",
60 "name": "Test Token",
61 "symbol": "TEST",
62 "uri": "https://test.com",
63 "uri_hash": "hash"
64 });
65
66 let metadata_null_denom_units: DenomMetadata =
67 serde_json::from_value(json_with_null_denom_units).unwrap();
68 assert_eq!(
69 metadata_null_denom_units.denom_units,
70 Vec::<DenomUnit>::new()
71 );
72
73 let json_with_units = json!({
75 "description": "Test Token",
76 "denom_units": [
77 {
78 "denom": "utest",
79 "exponent": 6,
80 "aliases": ["microtest"]
81 }
82 ],
83 "base": "utest",
84 "display": "TEST",
85 "name": "Test Token",
86 "symbol": "TEST",
87 "uri": "https://test.com",
88 "uri_hash": "hash"
89 });
90
91 let metadata_with_units: DenomMetadata = serde_json::from_value(json_with_units).unwrap();
92 assert_eq!(metadata_with_units.denom_units.len(), 1);
93 assert_eq!(metadata_with_units.denom_units[0].denom, "utest");
94
95 let json_with_null_aliases = json!({
97 "description": "Test Token",
98 "denom_units": [
99 {
100 "denom": "utest",
101 "exponent": 6,
102 "aliases": null
103 }
104 ],
105 "base": "utest",
106 "display": "TEST",
107 "name": "Test Token",
108 "symbol": "TEST",
109 "uri": "https://test.com",
110 "uri_hash": "hash"
111 });
112
113 let metadata_with_null_aliases: DenomMetadata =
114 serde_json::from_value(json_with_null_aliases).unwrap();
115 assert_eq!(metadata_with_null_aliases.denom_units.len(), 1);
116 assert_eq!(
117 metadata_with_null_aliases.denom_units[0].aliases,
118 Vec::<String>::new()
119 );
120 }
121
122 #[test]
123 fn deserialize_denom_metadata_with_missing_fields_fails() {
124 let json_missing_denom_units = json!({
126 "description": "Test Token",
127 "base": "utest",
128 "display": "TEST",
129 "name": "Test Token",
130 "symbol": "TEST",
131 "uri": "https://test.com",
132 "uri_hash": "hash"
133 });
134
135 let metadata: Result<DenomMetadata, Error> =
136 serde_json::from_value(json_missing_denom_units);
137 assert!(metadata.is_err());
138
139 let json_missing_alias = json!({
140 "description": "Test Token",
141 "base": "utest",
142 "denom_units": [
143 {
144 "denom": "utest",
145 "exponent": 6,
146 }
147 ],
148 "display": "TEST",
149 "name": "Test Token",
150 "symbol": "TEST",
151 "uri": "https://test.com",
152 "uri_hash": "hash"
153 });
154
155 let metadata_missing_alias: Result<DenomMetadata, Error> =
156 serde_json::from_value(json_missing_alias);
157 assert!(metadata_missing_alias.is_err());
158 }
159
160 #[test]
161 fn query_denom_metadata_with_null_denom_units_works() {
162 let json_with_null_denom_units = json!({
164 "description": "Test Token",
165 "denom_units": null,
166 "base": "utest",
167 "display": "TEST",
168 "name": "Test Token",
169 "symbol": "TEST",
170 "uri": "https://test.com",
171 "uri_hash": "hash"
172 });
173
174 let metadata_with_null_denom_units: DenomMetadata =
175 serde_json::from_value(json_with_null_denom_units).unwrap();
176 assert_eq!(
177 metadata_with_null_denom_units.denom_units,
178 Vec::<DenomUnit>::new()
179 );
180
181 let json_with_units = json!({
183 "description": "Test Token",
184 "denom_units": [
185 {
186 "denom": "utest",
187 "exponent": 6,
188 "aliases": ["microtest"]
189 }
190 ],
191 "base": "utest",
192 "display": "TEST",
193 "name": "Test Token",
194 "symbol": "TEST",
195 "uri": "https://test.com",
196 "uri_hash": "hash"
197 });
198
199 let metadata_with_units: DenomMetadata = serde_json::from_value(json_with_units).unwrap();
200 assert_eq!(metadata_with_units.denom_units.len(), 1);
201 assert_eq!(metadata_with_units.denom_units[0].denom, "utest");
202 assert_eq!(metadata_with_units.denom_units[0].aliases.len(), 1);
203 assert_eq!(metadata_with_units.denom_units[0].aliases[0], "microtest");
204
205 let json_with_null_aliases = json!({
207 "description": "Test Token",
208 "denom_units": [
209 {
210 "denom": "utest",
211 "exponent": 6,
212 "aliases": null
213 }
214 ],
215 "base": "utest",
216 "display": "TEST",
217 "name": "Test Token",
218 "symbol": "TEST",
219 "uri": "https://test.com",
220 "uri_hash": "hash"
221 });
222
223 let metadata_with_null_aliases: DenomMetadata =
224 serde_json::from_value(json_with_null_aliases).unwrap();
225 assert_eq!(metadata_with_null_aliases.denom_units.len(), 1);
226 assert_eq!(
227 metadata_with_null_aliases.denom_units[0].aliases,
228 Vec::<String>::new()
229 );
230 }
231
232 #[test]
233 fn query_denom_metadata_with_missing_fields_fails() {
234 let json_missing_denom_units = json!({
236 "description": "Test Token",
237 "base": "utest",
238 "display": "TEST",
239 "name": "Test Token",
240 "symbol": "TEST",
241 "uri": "https://test.com",
242 "uri_hash": "hash"
243 });
244
245 let json_missing_denom_units_metadata: Result<DenomMetadata, Error> =
246 serde_json::from_value(json_missing_denom_units);
247 assert!(json_missing_denom_units_metadata.is_err());
248
249 let json_missing_aliases = json!({
251 "description": "Test Token",
252 "denom_units": [
253 {
254 "denom": "utest",
255 "exponent": 6
256 }
257 ],
258 "base": "utest",
259 "display": "TEST",
260 "name": "Test Token",
261 "symbol": "TEST",
262 "uri": "https://test.com",
263 "uri_hash": "hash"
264 });
265
266 let missing_aliases_metadata: Result<DenomMetadata, Error> =
267 serde_json::from_value(json_missing_aliases);
268 assert!(missing_aliases_metadata.is_err());
269 }
270
271 #[test]
272 fn query_denom_metadata_with_mixed_null_and_value_works() {
273 let mixed_json = json!({
275 "description": "Mixed Token",
276 "denom_units": [
277 {
278 "denom": "unit1",
279 "exponent": 0,
280 "aliases": null
281 },
282 {
283 "denom": "unit2",
284 "exponent": 6,
285 "aliases": ["microunit", "u"]
286 },
287 {
288 "denom": "unit3",
289 "exponent": 9,
290 "aliases": []
291 }
292 ],
293 "base": "unit1",
294 "display": "MIXED",
295 "name": "Mixed Token",
296 "symbol": "MIX",
297 "uri": "https://mixed.token",
298 "uri_hash": "hash123"
299 });
300
301 let metadata: DenomMetadata = serde_json::from_value(mixed_json).unwrap();
302
303 assert!(metadata.denom_units[0].aliases.is_empty());
305
306 assert_eq!(metadata.denom_units[1].aliases.len(), 2);
308 assert_eq!(metadata.denom_units[1].aliases[0], "microunit");
309 assert_eq!(metadata.denom_units[1].aliases[1], "u");
310
311 assert!(metadata.denom_units[2].aliases.is_empty());
313 }
314}