stellar_baselib/
asset.rs

1use std::{
2    cmp::Ordering,
3    str::{Chars, FromStr},
4};
5
6use crate::claimant::ClaimantBehavior;
7use crate::keypair::Keypair;
8use crate::xdr;
9use stellar_strkey::{
10    ed25519,
11    Strkey::{self, PublicKeyEd25519},
12};
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct Asset {
16    pub code: String,
17    pub issuer: Option<String>,
18}
19impl From<&Asset> for xdr::TrustLineAsset {
20    fn from(value: &Asset) -> Self {
21        value.to_trust_line_xdr_object()
22    }
23}
24impl From<Asset> for xdr::TrustLineAsset {
25    fn from(value: Asset) -> Self {
26        value.to_trust_line_xdr_object()
27    }
28}
29impl From<&Asset> for xdr::ChangeTrustAsset {
30    fn from(value: &Asset) -> Self {
31        value.to_change_trust_xdr_object()
32    }
33}
34impl From<Asset> for xdr::ChangeTrustAsset {
35    fn from(value: Asset) -> Self {
36        value.to_change_trust_xdr_object()
37    }
38}
39
40// Define a trait for Asset behavior
41pub trait AssetBehavior {
42    fn new(code: &str, issuer: Option<&str>) -> Result<Self, String>
43    where
44        Self: Sized;
45    fn from_operation(asset_xdr: xdr::Asset) -> Result<Self, String>
46    where
47        Self: Sized;
48    fn to_xdr_object(&self) -> xdr::Asset;
49    fn to_change_trust_xdr_object(&self) -> xdr::ChangeTrustAsset;
50    fn to_trust_line_xdr_object(&self) -> xdr::TrustLineAsset;
51    fn ascii_compare(a: &str, b: &str) -> i32;
52    fn native() -> Self
53    where
54        Self: Sized;
55    fn is_native(&self) -> bool;
56    fn compare(asset_a: &Self, asset_b: &Self) -> i32
57    where
58        Self: Sized;
59    fn get_asset_type(&self) -> String;
60    fn get_raw_asset_type(&self) -> Result<xdr::AssetType, String>;
61    fn equals(&self, asset: &Self) -> bool;
62    fn get_code(&self) -> Option<String>;
63    fn get_issuer(&self) -> Option<String>;
64    fn to_string_asset(&self) -> String;
65}
66
67impl AssetBehavior for Asset {
68    fn new(code: &str, issuer: Option<&str>) -> Result<Self, String> {
69        if code.is_empty() || code.len() > 12 || !code.chars().all(|c| c.is_ascii_alphanumeric()) {
70            return Err(
71                "Asset code is invalid (maximum alphanumeric, 12 characters at max)".to_string(),
72            );
73        }
74
75        if code.to_lowercase() != "xlm" && issuer.is_none() {
76            return Err("Issuer cannot be null".to_string());
77        }
78
79        if let Some(issuer) = issuer {
80            if Strkey::from_str(issuer).is_err() {
81                return Err("Not a valid ed25519 public key".to_string());
82            }
83        }
84
85        let code = if code.to_lowercase() == "xlm" {
86            "XLM".to_string()
87        } else {
88            code.to_string()
89        };
90
91        Ok(Self {
92            code,
93            issuer: issuer.map(String::from),
94        })
95    }
96
97    fn from_operation(asset_xdr: xdr::Asset) -> Result<Asset, String> {
98        match asset_xdr {
99            xdr::Asset::Native => Ok(Asset::native()),
100            xdr::Asset::CreditAlphanum4(alpha_num_4) => {
101                let issuer = alpha_num_4.issuer.to_string();
102                let code = alpha_num_4.asset_code.to_string();
103                Ok(Asset::new(&code, Some(&issuer))?)
104            }
105            xdr::Asset::CreditAlphanum12(alpha_num_12) => {
106                let issuer = alpha_num_12.issuer.to_string();
107                let code = alpha_num_12.asset_code.to_string();
108                Ok(Asset::new(&code, Some(&issuer))?)
109            }
110            _ => Err(format!("Invalid asset type: {:?}", asset_xdr)),
111        }
112    }
113
114    fn to_trust_line_xdr_object(&self) -> xdr::TrustLineAsset {
115        if self.is_native() {
116            xdr::TrustLineAsset::Native
117        } else if self.code.len() <= 4 {
118            let asset_code = xdr::AssetCode4::from_str(&self.code).expect("Asset code is invalid");
119            let issuer = xdr::AccountId::from_str(
120                &self
121                    .issuer
122                    .clone()
123                    .expect("Issuer is None while not native"),
124            )
125            .expect("Issuer is invalid");
126
127            xdr::TrustLineAsset::CreditAlphanum4(xdr::AlphaNum4 { asset_code, issuer })
128        } else {
129            let asset_code = xdr::AssetCode12::from_str(&self.code).expect("Asset code is invalid");
130            let issuer = xdr::AccountId::from_str(
131                &self
132                    .issuer
133                    .clone()
134                    .expect("Issuer is None while not native"),
135            )
136            .expect("Issuer is invalid");
137
138            xdr::TrustLineAsset::CreditAlphanum12(xdr::AlphaNum12 { asset_code, issuer })
139        }
140    }
141
142    fn to_change_trust_xdr_object(&self) -> xdr::ChangeTrustAsset {
143        if self.is_native() {
144            xdr::ChangeTrustAsset::Native
145        } else if self.code.len() <= 4 {
146            let asset_code = xdr::AssetCode4::from_str(&self.code).expect("Asset code is invalid");
147            let issuer = xdr::AccountId::from_str(
148                &self
149                    .issuer
150                    .clone()
151                    .expect("Issuer is None while not native"),
152            )
153            .expect("Issuer is invalid");
154            xdr::ChangeTrustAsset::CreditAlphanum4(xdr::AlphaNum4 { asset_code, issuer })
155        } else {
156            let asset_code = xdr::AssetCode12::from_str(&self.code).expect("Asset code is invalid");
157            let issuer = xdr::AccountId::from_str(
158                &self
159                    .issuer
160                    .clone()
161                    .expect("Issuer is None while not native"),
162            )
163            .expect("Issuer is invalid");
164            xdr::ChangeTrustAsset::CreditAlphanum12(xdr::AlphaNum12 { asset_code, issuer })
165        }
166    }
167
168    fn to_xdr_object(&self) -> xdr::Asset {
169        if self.is_native() {
170            xdr::Asset::Native
171        } else if self.code.len() <= 4 {
172            let asset_code = xdr::AssetCode4::from_str(&self.code).expect("Asset code is invalid");
173            let issuer = xdr::AccountId::from_str(
174                &self
175                    .issuer
176                    .clone()
177                    .expect("Issuer is None while not native"),
178            )
179            .expect("Issuer is invalid");
180            xdr::Asset::CreditAlphanum4(xdr::AlphaNum4 { asset_code, issuer })
181        } else {
182            let asset_code = xdr::AssetCode12::from_str(&self.code).expect("Asset code is invalid");
183            let issuer = xdr::AccountId::from_str(
184                &self
185                    .issuer
186                    .clone()
187                    .expect("Issuer is None while not native"),
188            )
189            .expect("Issuer is invalid");
190            xdr::Asset::CreditAlphanum12(xdr::AlphaNum12 { asset_code, issuer })
191        }
192    }
193
194    fn ascii_compare(a: &str, b: &str) -> i32 {
195        let result = a.as_bytes().cmp(b.as_bytes());
196        match result {
197            Ordering::Less => -1,
198            Ordering::Equal => 0,
199            Ordering::Greater => 1,
200        }
201    }
202
203    fn native() -> Self {
204        // The native asset in Stellar is represented by the code 'XLM' with no issuer.
205        Self {
206            code: "XLM".to_string(),
207            issuer: None,
208        }
209    }
210
211    fn is_native(&self) -> bool {
212        self.issuer.is_none()
213    }
214
215    fn compare(asset_a: &Asset, asset_b: &Asset) -> i32 {
216        if asset_a.equals(asset_b) {
217            return 0;
218        }
219
220        let xdr_a_type = asset_a.get_raw_asset_type();
221        let xdr_b_type = asset_b.get_raw_asset_type();
222
223        if xdr_a_type != xdr_b_type {
224            let result = xdr_a_type.cmp(&xdr_b_type);
225            if result == Ordering::Less {
226                return -1;
227            } else {
228                return 1;
229            }
230        }
231
232        let code_compare = Self::ascii_compare(
233            &asset_a.get_code().unwrap_or("".to_owned()),
234            &asset_b.get_code().unwrap_or("".to_owned()),
235        );
236        if code_compare != 0 {
237            return code_compare;
238        }
239
240        Self::ascii_compare(
241            &asset_a.get_issuer().unwrap_or("".to_owned()),
242            &asset_b.get_issuer().unwrap_or("".to_owned()),
243        )
244    }
245
246    fn get_asset_type(&self) -> String {
247        match self.get_raw_asset_type() {
248            Ok(xdr::AssetType::Native) => "native".to_string(),
249            Ok(xdr::AssetType::CreditAlphanum4) => "credit_alphanum4".to_string(),
250            Ok(xdr::AssetType::CreditAlphanum12) => "credit_alphanum12".to_string(),
251            _ => "unknown".to_string(),
252        }
253    }
254
255    fn get_raw_asset_type(&self) -> Result<xdr::AssetType, String> {
256        if self.is_native() {
257            Ok(xdr::AssetType::Native)
258        } else if self.code.len() <= 4 {
259            Ok(xdr::AssetType::CreditAlphanum4)
260        } else {
261            Ok(xdr::AssetType::CreditAlphanum12)
262        }
263    }
264
265    fn equals(&self, asset: &Asset) -> bool {
266        self.get_code() == asset.get_code() && self.get_issuer() == asset.get_issuer()
267    }
268
269    fn get_code(&self) -> Option<String> {
270        Some(self.code.clone())
271    }
272
273    fn get_issuer(&self) -> Option<String> {
274        self.issuer.clone()
275    }
276
277    fn to_string_asset(&self) -> String {
278        if self.is_native() {
279            return "native".to_string();
280        }
281
282        match (self.get_code(), self.get_issuer()) {
283            (Some(code), Some(issuer)) => format!("{}:{}", code, issuer),
284            _ => "".to_string(),
285        }
286    }
287}
288
289impl ToString for Asset {
290    fn to_string(&self) -> String {
291        if self.is_native() {
292            return "native".to_string();
293        }
294
295        match (self.get_code(), self.get_issuer()) {
296            (Some(code), Some(issuer)) => format!("{}:{}", code, issuer),
297            _ => "".to_string(),
298        }
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use crate::xdr::WriteXdr as _;
305
306    use super::Asset;
307    use crate::asset::AssetBehavior;
308    use crate::xdr;
309
310    #[test]
311    fn test_no_issuer_for_non_xlm_asset() {
312        let err_val = Asset::new("USD", None).unwrap_err();
313        assert_eq!(err_val, "Issuer cannot be null");
314    }
315
316    #[test]
317    fn test_invalid_asset_code() {
318        let err_val = Asset::new(
319            "",
320            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
321        )
322        .unwrap_err();
323        assert_eq!(
324            err_val,
325            "Asset code is invalid (maximum alphanumeric, 12 characters at max)"
326        );
327        let err_val = super::Asset::new(
328            "1234567890123",
329            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
330        )
331        .unwrap_err();
332        assert_eq!(
333            err_val,
334            "Asset code is invalid (maximum alphanumeric, 12 characters at max)"
335        );
336        let err_val = Asset::new(
337            "ab_",
338            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
339        )
340        .unwrap_err();
341        assert_eq!(
342            err_val,
343            "Asset code is invalid (maximum alphanumeric, 12 characters at max)"
344        );
345    }
346
347    #[test]
348    fn test_native_asset_code() {
349        let asset = Asset::native();
350        assert_eq!(asset.get_code().unwrap(), "XLM");
351    }
352
353    #[test]
354    fn test_asset_code() {
355        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
356        let asset = Asset::new("USD", Some(issuer)).unwrap();
357        assert_eq!(asset.get_code().unwrap(), "USD");
358    }
359
360    #[test]
361    fn test_native_asset_issuer() {
362        let asset = Asset::native();
363        assert!(asset.get_issuer().is_none());
364    }
365
366    #[test]
367    fn test_non_native_asset_issuer() {
368        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
369        let asset = Asset::new("USD", Some(issuer)).unwrap();
370        assert_eq!(asset.get_issuer(), Some(issuer.to_string()));
371    }
372
373    #[test]
374    fn test_native_asset_type() {
375        let asset = Asset::native();
376        assert_eq!(asset.get_asset_type(), "native");
377    }
378
379    #[test]
380    fn test_credit_alphanum4_asset_type() {
381        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
382        let asset = Asset::new("ABCD", Some(issuer)).unwrap();
383        assert_eq!(asset.get_asset_type(), "credit_alphanum4");
384    }
385
386    #[test]
387    fn test_credit_alphanum12_asset_type() {
388        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
389        let asset = Asset::new("ABCDEF", Some(issuer)).unwrap();
390        assert_eq!(asset.get_asset_type(), "credit_alphanum12");
391    }
392
393    #[test]
394    fn test_parse_native_asset() {
395        let asset = Asset::native();
396
397        // Test toXDRObject() for Asset
398        let xdr = asset.to_xdr_object();
399
400        assert_eq!(
401            String::from_utf8(xdr.to_xdr(xdr::Limits::none()).unwrap()),
402            String::from_utf8([0u8, 0u8, 0u8, 0u8].to_vec())
403        );
404
405        let xdr = asset.to_change_trust_xdr_object();
406        assert_eq!(
407            String::from_utf8(xdr.to_xdr(xdr::Limits::none()).unwrap()),
408            String::from_utf8([0u8, 0u8, 0u8, 0u8].to_vec())
409        );
410
411        // // Test toTrustLineXDRObject() for TrustLineAsset
412        let xdr = asset.to_trust_line_xdr_object();
413        assert_eq!(
414            String::from_utf8(xdr.to_xdr(xdr::Limits::none()).unwrap()),
415            String::from_utf8([0u8, 0u8, 0u8, 0u8].to_vec())
416        );
417    }
418
419    #[test]
420    fn test_parse_3_alphanum_asset() {
421        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
422        let asset = Asset::new("USD", Some(issuer)).unwrap();
423        let xdr = asset.to_xdr_object();
424
425        match xdr {
426            xdr::Asset::CreditAlphanum4(x) => {
427                assert_eq!(hex::encode(x.asset_code), hex::encode("USD\0".to_string()))
428            }
429            _ => panic!("Error"),
430        }
431
432        let xdr = asset.to_change_trust_xdr_object();
433        match xdr {
434            xdr::ChangeTrustAsset::CreditAlphanum4(x) => {
435                assert_eq!(hex::encode(x.asset_code), hex::encode("USD\0".to_string()))
436            }
437            _ => panic!("Error"),
438        }
439
440        let xdr = asset.to_trust_line_xdr_object();
441        match xdr {
442            xdr::TrustLineAsset::CreditAlphanum4(x) => {
443                assert_eq!(hex::encode(x.asset_code), hex::encode("USD\0".to_string()))
444            }
445            _ => panic!("Error"),
446        }
447    }
448
449    #[test]
450    fn test_parse_4_alphanum_asset() {
451        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
452        let asset = Asset::new("BART", Some(issuer)).unwrap();
453        let xdr = asset.to_xdr_object();
454
455        match xdr {
456            xdr::Asset::CreditAlphanum4(x) => {
457                assert_eq!(hex::encode(x.asset_code), hex::encode("BART".to_string()))
458            }
459            _ => panic!("Error"),
460        }
461
462        let xdr = asset.to_change_trust_xdr_object();
463        match xdr {
464            xdr::ChangeTrustAsset::CreditAlphanum4(x) => {
465                assert_eq!(hex::encode(x.asset_code), hex::encode("BART".to_string()))
466            }
467            _ => panic!("Error"),
468        }
469
470        let xdr = asset.to_trust_line_xdr_object();
471        match xdr {
472            xdr::TrustLineAsset::CreditAlphanum4(x) => {
473                assert_eq!(hex::encode(x.asset_code), hex::encode("BART".to_string()))
474            }
475            _ => panic!("Error"),
476        }
477    }
478
479    #[test]
480    fn test_parse_5_alphanum_asset() {
481        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
482        let asset = Asset::new("12345", Some(issuer)).unwrap();
483        let xdr = asset.to_xdr_object();
484
485        match xdr {
486            xdr::Asset::CreditAlphanum12(x) => assert_eq!(
487                hex::encode(x.asset_code),
488                hex::encode("12345\0\0\0\0\0\0\0".to_string())
489            ),
490            _ => panic!("Error"),
491        }
492
493        let xdr = asset.to_change_trust_xdr_object();
494        match xdr {
495            xdr::ChangeTrustAsset::CreditAlphanum12(x) => assert_eq!(
496                hex::encode(x.asset_code),
497                hex::encode("12345\0\0\0\0\0\0\0".to_string())
498            ),
499            _ => panic!("Error"),
500        }
501
502        let xdr = asset.to_trust_line_xdr_object();
503        match xdr {
504            xdr::TrustLineAsset::CreditAlphanum12(x) => assert_eq!(
505                hex::encode(x.asset_code),
506                hex::encode("12345\0\0\0\0\0\0\0".to_string())
507            ),
508            _ => panic!("Error"),
509        }
510    }
511
512    #[test]
513    fn test_parse_12_alphanum_asset() {
514        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
515        let asset = Asset::new("123456789012", Some(issuer)).unwrap();
516        let xdr = asset.to_xdr_object();
517
518        match xdr {
519            xdr::Asset::CreditAlphanum12(x) => assert_eq!(
520                hex::encode(x.asset_code),
521                hex::encode("123456789012".to_string())
522            ),
523            _ => panic!("Error"),
524        }
525
526        let xdr = asset.to_change_trust_xdr_object();
527        match xdr {
528            xdr::ChangeTrustAsset::CreditAlphanum12(x) => assert_eq!(
529                hex::encode(x.asset_code),
530                hex::encode("123456789012".to_string())
531            ),
532            _ => panic!("Error"),
533        }
534
535        let xdr = asset.to_trust_line_xdr_object();
536        match xdr {
537            xdr::TrustLineAsset::CreditAlphanum12(x) => assert_eq!(
538                hex::encode(x.asset_code),
539                hex::encode("123456789012".to_string())
540            ),
541            _ => panic!("Error"),
542        }
543    }
544
545    #[test]
546    fn test_parse_xdr_asset() {
547        let xdr = xdr::Asset::Native;
548        let asset = Asset::from_operation(xdr).unwrap();
549        assert!(asset.is_native());
550
551        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
552        let addr = xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
553            stellar_strkey::ed25519::PublicKey::from_string(issuer)
554                .unwrap()
555                .0,
556        )));
557
558        let mut asset_code: [u8; 4] = [0; 4];
559
560        for (i, b) in "KHL".as_bytes().iter().enumerate() {
561            asset_code[i] = *b;
562        }
563        let xdr = xdr::Asset::CreditAlphanum4(xdr::AlphaNum4 {
564            asset_code: xdr::AssetCode4(asset_code),
565            issuer: addr.clone(),
566        });
567
568        let asset = Asset::from_operation(xdr).unwrap();
569
570        assert_eq!("KHL", asset.get_code().unwrap());
571        assert_eq!(issuer.to_string(), asset.get_issuer().unwrap());
572    }
573
574    #[test]
575    fn test_parse_12_alphanum_xdr_asset() {
576        let issuer = "GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ";
577        let addr = xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
578            stellar_strkey::ed25519::PublicKey::from_string(issuer)
579                .unwrap()
580                .0,
581        )));
582        let mut asset_code: [u8; 12] = [0; 12];
583
584        for (i, b) in "KHLTOKEN".as_bytes().iter().enumerate() {
585            asset_code[i] = *b;
586        }
587        let xdr = xdr::Asset::CreditAlphanum12(xdr::AlphaNum12 {
588            asset_code: xdr::AssetCode12(asset_code),
589            issuer: addr.clone(),
590        });
591
592        let asset = Asset::from_operation(xdr).unwrap();
593        assert_eq!("KHLTOKEN", asset.get_code().unwrap());
594        assert_eq!(issuer.to_string(), asset.get_issuer().unwrap());
595    }
596
597    #[test]
598    fn test_to_string_native() {
599        let asset = Asset::native();
600        assert_eq!(asset.to_string(), "native");
601    }
602
603    #[test]
604    fn test_to_string_non_native() {
605        let asset = Asset::new(
606            "USD",
607            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
608        )
609        .unwrap();
610        assert_eq!(
611            asset.to_string(),
612            "USD:GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"
613        );
614    }
615
616    #[test]
617    fn test_compare_works() {
618        let asset_a = Asset::new(
619            "ARST",
620            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
621        )
622        .unwrap();
623
624        let asset_b = Asset::new(
625            "USD",
626            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
627        )
628        .unwrap();
629
630        Asset::compare(&asset_a, &asset_b);
631    }
632
633    #[test]
634    fn test_compare_equal_assets() {
635        let xlm = Asset::native();
636        let asset_a = Asset::new(
637            "ARST",
638            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
639        )
640        .unwrap();
641
642        let asset_b = Asset::new(
643            "USD",
644            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
645        )
646        .unwrap();
647
648        // println!("Result {:?}",Asset::compare(&xlm.clone(), &xlm).);
649        assert_eq!(Asset::compare(&xlm.clone(), &xlm), 0);
650        assert_eq!(Asset::compare(&asset_a.clone(), &asset_a), 0);
651        assert_eq!(Asset::compare(&asset_b.clone(), &asset_b), 0);
652    }
653
654    #[test]
655    fn test_compare_assets() {
656        let xlm = Asset::native();
657        let asset_a = Asset::new(
658            "ARST",
659            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
660        )
661        .unwrap();
662
663        let asset_b = Asset::new(
664            "ARSTANUM12",
665            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
666        )
667        .unwrap();
668
669        // println!("Result {:?}",Asset::compare(&xlm.clone(), &xlm).);
670        assert_eq!(Asset::compare(&xlm.clone(), &xlm), 0);
671        assert_eq!(Asset::compare(&xlm.clone(), &asset_a), -1);
672        assert_eq!(Asset::compare(&xlm.clone(), &asset_b), -1);
673
674        assert_eq!(Asset::compare(&asset_a.clone(), &xlm), 1);
675        assert_eq!(Asset::compare(&asset_a.clone(), &asset_a), 0);
676        assert_eq!(Asset::compare(&asset_a.clone(), &asset_b), -1);
677
678        assert_eq!(Asset::compare(&asset_b.clone(), &xlm), 1);
679        assert_eq!(Asset::compare(&asset_b.clone(), &asset_a), 1);
680        assert_eq!(Asset::compare(&asset_b.clone(), &asset_b), 0);
681    }
682
683    #[test]
684    fn test_compare_asset() {
685        let asset_arst = Asset::new(
686            "ARST",
687            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
688        )
689        .unwrap();
690
691        let asset_usdx = Asset::new(
692            "USDA",
693            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
694        )
695        .unwrap();
696
697        // println!("Result {:?}",Asset::compare(&xlm.clone(), &xlm).);
698        assert_eq!(Asset::compare(&asset_arst.clone(), &asset_arst), 0);
699        assert_eq!(Asset::compare(&asset_arst.clone(), &asset_usdx), -1);
700
701        assert_eq!(Asset::compare(&asset_usdx.clone(), &asset_arst), 1);
702        assert_eq!(Asset::compare(&asset_usdx.clone(), &asset_usdx), 0);
703
704        let asset_lower = Asset::new(
705            "aRST",
706            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
707        )
708        .unwrap();
709
710        assert_eq!(Asset::compare(&asset_arst.clone(), &asset_lower), -1);
711        assert_eq!(Asset::compare(&asset_lower.clone(), &asset_arst), 1);
712    }
713
714    #[test]
715    fn test_compare_asset_issuers() {
716        let asset_a = Asset::new(
717            "ARST",
718            Some("GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"),
719        )
720        .unwrap();
721
722        let asset_b = Asset::new(
723            "ARST",
724            Some("GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ"),
725        )
726        .unwrap();
727
728        assert_eq!(Asset::compare(&asset_a.clone(), &asset_b), -1);
729        assert_eq!(Asset::compare(&asset_a.clone(), &asset_a), 0);
730
731        assert_eq!(Asset::compare(&asset_b.clone(), &asset_a), 1);
732        assert_eq!(Asset::compare(&asset_b.clone(), &asset_b), 0);
733    }
734
735    #[test]
736    fn test_compare_upper_lower() {
737        let asset_a = Asset::new(
738            "B",
739            Some("GA7NLOF4EHWMJF6DBXXV2H6AYI7IHYWNFZR6R52BYBLY7TE5Q74AIDRA"),
740        )
741        .unwrap();
742
743        let asset_b = Asset::new(
744            "a",
745            Some("GA7NLOF4EHWMJF6DBXXV2H6AYI7IHYWNFZR6R52BYBLY7TE5Q74AIDRA"),
746        )
747        .unwrap();
748
749        assert_eq!(Asset::compare(&asset_a.clone(), &asset_b), -1);
750    }
751}