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