1pub fn encode_iou_amount(mantissa: u64, exponent: i8, positive: bool) -> [u8; 8] {
22 if mantissa == 0 {
23 let mut bytes = [0u8; 8];
25 bytes[0] = 0x80; return bytes;
27 }
28
29 let mut m = mantissa;
31 let mut e = exponent as i16;
32
33 while m < 1_000_000_000_000_000 && e > -96 {
35 m *= 10;
36 e -= 1;
37 }
38 while m >= 10_000_000_000_000_000 && e < 80 {
39 m /= 10;
40 e += 1;
41 }
42
43 let biased_exp = (e + 97) as u64;
45
46 let mut val: u64 = 0;
47 val |= 1 << 63; if positive {
49 val |= 1 << 62; }
51 val |= (biased_exp & 0xFF) << 54; val |= m & 0x003F_FFFF_FFFF_FFFF; val.to_be_bytes()
55}
56
57pub fn decode_iou_amount(bytes: &[u8; 8]) -> (u64, i8, bool) {
61 let val = u64::from_be_bytes(*bytes);
62
63 if val & 0x003F_FFFF_FFFF_FFFF == 0 {
65 return (0, 0, true);
66 }
67
68 let positive = (val >> 62) & 1 == 1;
69 let biased_exp = ((val >> 54) & 0xFF) as i16;
70 let exponent = (biased_exp - 97) as i8;
71 let mantissa = val & 0x003F_FFFF_FFFF_FFFF;
72
73 (mantissa, exponent, positive)
74}
75
76pub fn encode_currency_code(code: &str) -> Result<[u8; 20], &'static str> {
82 if code.len() != 3 {
83 return Err("currency code must be 3 characters");
84 }
85 if code == "XRP" {
86 return Err("XRP is not an issued currency");
87 }
88
89 let mut out = [0u8; 20];
90 out[12..15].copy_from_slice(code.as_bytes());
91 Ok(out)
92}
93
94pub const TT_OFFER_CREATE: u16 = 7;
100pub const TT_OFFER_CANCEL: u16 = 8;
102
103pub fn offer_create(
113 account: &[u8; 20],
114 taker_gets_drops: u64,
115 taker_pays_drops: u64,
116 sequence: u32,
117 fee_drops: u64,
118 flags: u32,
119) -> Vec<u8> {
120 let mut buf = Vec::with_capacity(100);
121
122 buf.extend_from_slice(&[0x12, (TT_OFFER_CREATE >> 8) as u8, TT_OFFER_CREATE as u8]);
124 buf.extend_from_slice(&[0x22]);
126 buf.extend_from_slice(&flags.to_be_bytes());
127 buf.extend_from_slice(&[0x24]);
129 buf.extend_from_slice(&sequence.to_be_bytes());
130 buf.push(0x68);
132 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
133 buf.push(0x64);
135 buf.extend_from_slice(&encode_xrp_amount(taker_pays_drops));
136 buf.push(0x65);
138 buf.extend_from_slice(&encode_xrp_amount(taker_gets_drops));
139 buf.extend_from_slice(&[0x81, 0x14]);
141 buf.extend_from_slice(account);
142
143 buf
144}
145
146pub fn offer_cancel(
148 account: &[u8; 20],
149 offer_sequence: u32,
150 sequence: u32,
151 fee_drops: u64,
152) -> Vec<u8> {
153 let mut buf = Vec::with_capacity(60);
154
155 buf.extend_from_slice(&[0x12, (TT_OFFER_CANCEL >> 8) as u8, TT_OFFER_CANCEL as u8]);
156 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]); buf.extend_from_slice(&[0x24]);
158 buf.extend_from_slice(&sequence.to_be_bytes());
159 buf.extend_from_slice(&[0x20, 0x19]);
161 buf.extend_from_slice(&offer_sequence.to_be_bytes());
162 buf.push(0x68);
163 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
164 buf.extend_from_slice(&[0x81, 0x14]);
165 buf.extend_from_slice(account);
166
167 buf
168}
169
170pub const TT_ESCROW_CREATE: u16 = 1;
176pub const TT_ESCROW_FINISH: u16 = 2;
178pub const TT_ESCROW_CANCEL: u16 = 4;
180
181pub fn escrow_create(
192 account: &[u8; 20],
193 destination: &[u8; 20],
194 amount_drops: u64,
195 finish_after: u32,
196 cancel_after: Option<u32>,
197 sequence: u32,
198 fee_drops: u64,
199) -> Vec<u8> {
200 let mut buf = Vec::with_capacity(80);
201
202 buf.extend_from_slice(&[0x12, (TT_ESCROW_CREATE >> 8) as u8, TT_ESCROW_CREATE as u8]);
203 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]); buf.extend_from_slice(&[0x24]);
205 buf.extend_from_slice(&sequence.to_be_bytes());
206 buf.extend_from_slice(&[0x20, 0x24]);
208 buf.extend_from_slice(&finish_after.to_be_bytes());
209 if let Some(cancel) = cancel_after {
211 buf.extend_from_slice(&[0x20, 0x25]);
212 buf.extend_from_slice(&cancel.to_be_bytes());
213 }
214 buf.push(0x61);
216 buf.extend_from_slice(&encode_xrp_amount(amount_drops));
217 buf.push(0x68);
219 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
220 buf.extend_from_slice(&[0x81, 0x14]);
222 buf.extend_from_slice(account);
223 buf.extend_from_slice(&[0x83, 0x14]);
225 buf.extend_from_slice(destination);
226
227 buf
228}
229
230pub fn escrow_finish(
232 account: &[u8; 20],
233 owner: &[u8; 20],
234 offer_sequence: u32,
235 sequence: u32,
236 fee_drops: u64,
237) -> Vec<u8> {
238 let mut buf = Vec::with_capacity(70);
239
240 buf.extend_from_slice(&[0x12, (TT_ESCROW_FINISH >> 8) as u8, TT_ESCROW_FINISH as u8]);
241 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
242 buf.extend_from_slice(&[0x24]);
243 buf.extend_from_slice(&sequence.to_be_bytes());
244 buf.extend_from_slice(&[0x20, 0x19]); buf.extend_from_slice(&offer_sequence.to_be_bytes());
246 buf.push(0x68);
247 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
248 buf.extend_from_slice(&[0x81, 0x14]);
249 buf.extend_from_slice(account);
250 buf.extend_from_slice(&[0x82, 0x14]);
252 buf.extend_from_slice(owner);
253
254 buf
255}
256
257pub fn escrow_cancel(
259 account: &[u8; 20],
260 owner: &[u8; 20],
261 offer_sequence: u32,
262 sequence: u32,
263 fee_drops: u64,
264) -> Vec<u8> {
265 let mut buf = Vec::with_capacity(70);
266
267 buf.extend_from_slice(&[0x12, (TT_ESCROW_CANCEL >> 8) as u8, TT_ESCROW_CANCEL as u8]);
268 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
269 buf.extend_from_slice(&[0x24]);
270 buf.extend_from_slice(&sequence.to_be_bytes());
271 buf.extend_from_slice(&[0x20, 0x19]);
272 buf.extend_from_slice(&offer_sequence.to_be_bytes());
273 buf.push(0x68);
274 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
275 buf.extend_from_slice(&[0x81, 0x14]);
276 buf.extend_from_slice(account);
277 buf.extend_from_slice(&[0x82, 0x14]);
278 buf.extend_from_slice(owner);
279
280 buf
281}
282
283fn encode_xrp_amount(drops: u64) -> [u8; 8] {
289 let val = drops | (0x40 << 56); val.to_be_bytes()
291}
292
293pub const TT_ACCOUNT_SET: u16 = 3;
299
300pub mod account_set_flags {
302 pub const ASF_REQUIRE_DEST: u32 = 1;
304 pub const ASF_REQUIRE_AUTH: u32 = 2;
306 pub const ASF_DISALLOW_XRP: u32 = 3;
308 pub const ASF_DISABLE_MASTER: u32 = 4;
310 pub const ASF_NO_FREEZE: u32 = 6;
312 pub const ASF_GLOBAL_FREEZE: u32 = 7;
314 pub const ASF_DEPOSIT_AUTH: u32 = 9;
316 pub const ASF_ALLOW_CLAWBACK: u32 = 16;
318}
319
320pub fn account_set(
322 account: &[u8; 20],
323 set_flag: Option<u32>,
324 clear_flag: Option<u32>,
325 sequence: u32,
326 fee_drops: u64,
327) -> Vec<u8> {
328 let mut buf = Vec::with_capacity(60);
329 buf.extend_from_slice(&[0x12, (TT_ACCOUNT_SET >> 8) as u8, TT_ACCOUNT_SET as u8]);
330 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
331 buf.extend_from_slice(&[0x24]);
332 buf.extend_from_slice(&sequence.to_be_bytes());
333 if let Some(flag) = set_flag {
334 buf.extend_from_slice(&[0x20, 0x21]);
335 buf.extend_from_slice(&flag.to_be_bytes());
336 }
337 if let Some(flag) = clear_flag {
338 buf.extend_from_slice(&[0x20, 0x22]);
339 buf.extend_from_slice(&flag.to_be_bytes());
340 }
341 buf.push(0x68);
342 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
343 buf.extend_from_slice(&[0x81, 0x14]);
344 buf.extend_from_slice(account);
345 buf
346}
347
348pub const TT_CHANNEL_CREATE: u16 = 13;
354pub const TT_CHANNEL_FUND: u16 = 14;
356pub const TT_CHANNEL_CLAIM: u16 = 15;
358
359pub fn channel_create(
361 account: &[u8; 20],
362 destination: &[u8; 20],
363 amount_drops: u64,
364 settle_delay: u32,
365 public_key: &[u8; 33],
366 sequence: u32,
367 fee_drops: u64,
368) -> Vec<u8> {
369 let mut buf = Vec::with_capacity(120);
370 buf.extend_from_slice(&[
371 0x12,
372 (TT_CHANNEL_CREATE >> 8) as u8,
373 TT_CHANNEL_CREATE as u8,
374 ]);
375 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
376 buf.extend_from_slice(&[0x24]);
377 buf.extend_from_slice(&sequence.to_be_bytes());
378 buf.extend_from_slice(&[0x20, 0x27]);
379 buf.extend_from_slice(&settle_delay.to_be_bytes());
380 buf.push(0x61);
381 buf.extend_from_slice(&encode_xrp_amount(amount_drops));
382 buf.push(0x68);
383 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
384 buf.extend_from_slice(&[0x71, 0x03]);
385 buf.push(public_key.len() as u8);
386 buf.extend_from_slice(public_key);
387 buf.extend_from_slice(&[0x81, 0x14]);
388 buf.extend_from_slice(account);
389 buf.extend_from_slice(&[0x83, 0x14]);
390 buf.extend_from_slice(destination);
391 buf
392}
393
394pub fn channel_fund(
396 account: &[u8; 20],
397 channel_id: &[u8; 32],
398 amount_drops: u64,
399 sequence: u32,
400 fee_drops: u64,
401) -> Vec<u8> {
402 let mut buf = Vec::with_capacity(80);
403 buf.extend_from_slice(&[0x12, (TT_CHANNEL_FUND >> 8) as u8, TT_CHANNEL_FUND as u8]);
404 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
405 buf.extend_from_slice(&[0x24]);
406 buf.extend_from_slice(&sequence.to_be_bytes());
407 buf.push(0x61);
408 buf.extend_from_slice(&encode_xrp_amount(amount_drops));
409 buf.push(0x68);
410 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
411 buf.extend_from_slice(&[0x50, 0x16]);
412 buf.extend_from_slice(channel_id);
413 buf.extend_from_slice(&[0x81, 0x14]);
414 buf.extend_from_slice(account);
415 buf
416}
417
418pub fn channel_claim(
420 account: &[u8; 20],
421 channel_id: &[u8; 32],
422 balance_drops: Option<u64>,
423 sequence: u32,
424 fee_drops: u64,
425) -> Vec<u8> {
426 let mut buf = Vec::with_capacity(80);
427 buf.extend_from_slice(&[0x12, (TT_CHANNEL_CLAIM >> 8) as u8, TT_CHANNEL_CLAIM as u8]);
428 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
429 buf.extend_from_slice(&[0x24]);
430 buf.extend_from_slice(&sequence.to_be_bytes());
431 if let Some(balance) = balance_drops {
432 buf.push(0x61);
433 buf.extend_from_slice(&encode_xrp_amount(balance));
434 }
435 buf.push(0x68);
436 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
437 buf.extend_from_slice(&[0x50, 0x16]);
438 buf.extend_from_slice(channel_id);
439 buf.extend_from_slice(&[0x81, 0x14]);
440 buf.extend_from_slice(account);
441 buf
442}
443
444pub const TT_NFTOKEN_MINT: u16 = 25;
450pub const TT_NFTOKEN_CREATE_OFFER: u16 = 27;
452pub const TT_NFTOKEN_ACCEPT_OFFER: u16 = 29;
454pub const TT_NFTOKEN_BURN: u16 = 26;
456
457pub mod nftoken_flags {
459 pub const TF_TRANSFERABLE: u32 = 0x0008;
461 pub const TF_BURNABLE: u32 = 0x0001;
463 pub const TF_ONLY_XRP: u32 = 0x0002;
465}
466
467pub fn nftoken_mint(
469 account: &[u8; 20],
470 nftoken_taxon: u32,
471 flags: u32,
472 uri: Option<&[u8]>,
473 sequence: u32,
474 fee_drops: u64,
475) -> Vec<u8> {
476 let mut buf = Vec::with_capacity(100);
477 buf.extend_from_slice(&[0x12, (TT_NFTOKEN_MINT >> 8) as u8, TT_NFTOKEN_MINT as u8]);
478 buf.extend_from_slice(&[0x22]);
479 buf.extend_from_slice(&flags.to_be_bytes());
480 buf.extend_from_slice(&[0x24]);
481 buf.extend_from_slice(&sequence.to_be_bytes());
482 buf.extend_from_slice(&[0x20, 0x2A]);
483 buf.extend_from_slice(&nftoken_taxon.to_be_bytes());
484 buf.push(0x68);
485 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
486 if let Some(uri_bytes) = uri {
487 buf.extend_from_slice(&[0x75, 0x0D]);
488 buf.push(uri_bytes.len() as u8);
489 buf.extend_from_slice(uri_bytes);
490 }
491 buf.extend_from_slice(&[0x81, 0x14]);
492 buf.extend_from_slice(account);
493 buf
494}
495
496pub fn nftoken_create_offer(
498 account: &[u8; 20],
499 nftoken_id: &[u8; 32],
500 amount_drops: u64,
501 flags: u32,
502 destination: Option<&[u8; 20]>,
503 sequence: u32,
504 fee_drops: u64,
505) -> Vec<u8> {
506 let mut buf = Vec::with_capacity(120);
507 buf.extend_from_slice(&[
508 0x12,
509 (TT_NFTOKEN_CREATE_OFFER >> 8) as u8,
510 TT_NFTOKEN_CREATE_OFFER as u8,
511 ]);
512 buf.extend_from_slice(&[0x22]);
513 buf.extend_from_slice(&flags.to_be_bytes());
514 buf.extend_from_slice(&[0x24]);
515 buf.extend_from_slice(&sequence.to_be_bytes());
516 buf.push(0x61);
517 buf.extend_from_slice(&encode_xrp_amount(amount_drops));
518 buf.push(0x68);
519 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
520 buf.extend_from_slice(&[0x50, 0x2A]);
521 buf.extend_from_slice(nftoken_id);
522 buf.extend_from_slice(&[0x81, 0x14]);
523 buf.extend_from_slice(account);
524 if let Some(dest) = destination {
525 buf.extend_from_slice(&[0x83, 0x14]);
526 buf.extend_from_slice(dest);
527 }
528 buf
529}
530
531pub fn nftoken_accept_offer(
533 account: &[u8; 20],
534 sell_offer: Option<&[u8; 32]>,
535 buy_offer: Option<&[u8; 32]>,
536 sequence: u32,
537 fee_drops: u64,
538) -> Vec<u8> {
539 let mut buf = Vec::with_capacity(100);
540 buf.extend_from_slice(&[
541 0x12,
542 (TT_NFTOKEN_ACCEPT_OFFER >> 8) as u8,
543 TT_NFTOKEN_ACCEPT_OFFER as u8,
544 ]);
545 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
546 buf.extend_from_slice(&[0x24]);
547 buf.extend_from_slice(&sequence.to_be_bytes());
548 buf.push(0x68);
549 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
550 if let Some(offer) = sell_offer {
551 buf.extend_from_slice(&[0x50, 0x29]);
552 buf.extend_from_slice(offer);
553 }
554 if let Some(offer) = buy_offer {
555 buf.extend_from_slice(&[0x50, 0x28]);
556 buf.extend_from_slice(offer);
557 }
558 buf.extend_from_slice(&[0x81, 0x14]);
559 buf.extend_from_slice(account);
560 buf
561}
562
563pub fn nftoken_burn(
565 account: &[u8; 20],
566 nftoken_id: &[u8; 32],
567 sequence: u32,
568 fee_drops: u64,
569) -> Vec<u8> {
570 let mut buf = Vec::with_capacity(80);
571 buf.extend_from_slice(&[0x12, (TT_NFTOKEN_BURN >> 8) as u8, TT_NFTOKEN_BURN as u8]);
572 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
573 buf.extend_from_slice(&[0x24]);
574 buf.extend_from_slice(&sequence.to_be_bytes());
575 buf.push(0x68);
576 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
577 buf.extend_from_slice(&[0x50, 0x2A]);
578 buf.extend_from_slice(nftoken_id);
579 buf.extend_from_slice(&[0x81, 0x14]);
580 buf.extend_from_slice(account);
581 buf
582}
583
584pub const TT_CHECK_CREATE: u16 = 16;
590pub const TT_CHECK_CASH: u16 = 17;
592pub const TT_CHECK_CANCEL: u16 = 18;
594
595pub fn check_create(
597 account: &[u8; 20],
598 destination: &[u8; 20],
599 send_max_drops: u64,
600 sequence: u32,
601 fee_drops: u64,
602) -> Vec<u8> {
603 let mut buf = Vec::with_capacity(80);
604 buf.extend_from_slice(&[0x12, (TT_CHECK_CREATE >> 8) as u8, TT_CHECK_CREATE as u8]);
605 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
606 buf.extend_from_slice(&[0x24]);
607 buf.extend_from_slice(&sequence.to_be_bytes());
608 buf.push(0x69);
609 buf.extend_from_slice(&encode_xrp_amount(send_max_drops));
610 buf.push(0x68);
611 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
612 buf.extend_from_slice(&[0x81, 0x14]);
613 buf.extend_from_slice(account);
614 buf.extend_from_slice(&[0x83, 0x14]);
615 buf.extend_from_slice(destination);
616 buf
617}
618
619pub fn check_cash(
621 account: &[u8; 20],
622 check_id: &[u8; 32],
623 amount_drops: u64,
624 sequence: u32,
625 fee_drops: u64,
626) -> Vec<u8> {
627 let mut buf = Vec::with_capacity(80);
628 buf.extend_from_slice(&[0x12, (TT_CHECK_CASH >> 8) as u8, TT_CHECK_CASH as u8]);
629 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
630 buf.extend_from_slice(&[0x24]);
631 buf.extend_from_slice(&sequence.to_be_bytes());
632 buf.push(0x61);
633 buf.extend_from_slice(&encode_xrp_amount(amount_drops));
634 buf.push(0x68);
635 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
636 buf.extend_from_slice(&[0x50, 0x18]);
637 buf.extend_from_slice(check_id);
638 buf.extend_from_slice(&[0x81, 0x14]);
639 buf.extend_from_slice(account);
640 buf
641}
642
643pub fn check_cancel(
645 account: &[u8; 20],
646 check_id: &[u8; 32],
647 sequence: u32,
648 fee_drops: u64,
649) -> Vec<u8> {
650 let mut buf = Vec::with_capacity(70);
651 buf.extend_from_slice(&[0x12, (TT_CHECK_CANCEL >> 8) as u8, TT_CHECK_CANCEL as u8]);
652 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
653 buf.extend_from_slice(&[0x24]);
654 buf.extend_from_slice(&sequence.to_be_bytes());
655 buf.push(0x68);
656 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
657 buf.extend_from_slice(&[0x50, 0x18]);
658 buf.extend_from_slice(check_id);
659 buf.extend_from_slice(&[0x81, 0x14]);
660 buf.extend_from_slice(account);
661 buf
662}
663
664pub const TT_SET_HOOK: u16 = 22;
670
671pub fn set_hook(
673 account: &[u8; 20],
674 hook_hash: &[u8; 32],
675 sequence: u32,
676 fee_drops: u64,
677) -> Vec<u8> {
678 let mut buf = Vec::with_capacity(100);
679 buf.extend_from_slice(&[0x12, (TT_SET_HOOK >> 8) as u8, TT_SET_HOOK as u8]);
680 buf.extend_from_slice(&[0x22, 0x00, 0x00, 0x00, 0x00]);
681 buf.extend_from_slice(&[0x24]);
682 buf.extend_from_slice(&sequence.to_be_bytes());
683 buf.push(0x68);
684 buf.extend_from_slice(&encode_xrp_amount(fee_drops));
685 buf.extend_from_slice(&[0x50, 0x20]);
686 buf.extend_from_slice(hook_hash);
687 buf.extend_from_slice(&[0x81, 0x14]);
688 buf.extend_from_slice(account);
689 buf
690}
691
692#[cfg(test)]
697#[allow(clippy::unwrap_used, clippy::expect_used)]
698mod tests {
699 use super::*;
700
701 const ACCOUNT: [u8; 20] = [0x01; 20];
702 const DEST: [u8; 20] = [0x02; 20];
703
704 #[test]
707 fn test_iou_encode_decode_roundtrip() {
708 let encoded = encode_iou_amount(1_000_000_000_000_000, 0, true);
709 let (m, e, pos) = decode_iou_amount(&encoded);
710 assert!(pos);
711 let original = 1_000_000_000_000_000u128 * 10u128.pow(0);
713 let decoded = m as u128 * 10u128.pow((e + 97 - 97) as u32);
714 assert!(m > 0);
716 }
717
718 #[test]
719 fn test_iou_zero() {
720 let encoded = encode_iou_amount(0, 0, true);
721 assert_eq!(encoded[0] & 0x80, 0x80); let (m, _, _) = decode_iou_amount(&encoded);
723 assert_eq!(m, 0);
724 }
725
726 #[test]
727 fn test_iou_negative() {
728 let encoded = encode_iou_amount(1_000_000_000_000_000, 0, false);
729 let (_, _, pos) = decode_iou_amount(&encoded);
730 assert!(!pos);
731 }
732
733 #[test]
736 fn test_currency_code_usd() {
737 let cc = encode_currency_code("USD").unwrap();
738 assert_eq!(&cc[12..15], b"USD");
739 assert_eq!(&cc[..12], &[0u8; 12]);
740 }
741
742 #[test]
743 fn test_currency_code_xrp_rejected() {
744 assert!(encode_currency_code("XRP").is_err());
745 }
746
747 #[test]
748 fn test_currency_code_wrong_length() {
749 assert!(encode_currency_code("US").is_err());
750 assert!(encode_currency_code("USDC").is_err());
751 }
752
753 #[test]
756 fn test_offer_create_serialization() {
757 let tx = offer_create(&ACCOUNT, 1_000_000, 500_000, 42, 12, 0);
758 assert!(!tx.is_empty());
759 assert_eq!(tx[0], 0x12);
761 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_OFFER_CREATE);
762 }
763
764 #[test]
765 fn test_offer_cancel_serialization() {
766 let tx = offer_cancel(&ACCOUNT, 10, 43, 12);
767 assert!(!tx.is_empty());
768 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_OFFER_CANCEL);
769 }
770
771 #[test]
774 fn test_escrow_create_serialization() {
775 let tx = escrow_create(
776 &ACCOUNT,
777 &DEST,
778 1_000_000,
779 1700000000,
780 Some(1700100000),
781 44,
782 12,
783 );
784 assert!(!tx.is_empty());
785 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_ESCROW_CREATE);
786 }
787
788 #[test]
789 fn test_escrow_create_no_cancel() {
790 let tx1 = escrow_create(&ACCOUNT, &DEST, 1_000_000, 1700000000, None, 44, 12);
791 let tx2 = escrow_create(
792 &ACCOUNT,
793 &DEST,
794 1_000_000,
795 1700000000,
796 Some(1700100000),
797 44,
798 12,
799 );
800 assert!(tx2.len() > tx1.len());
802 }
803
804 #[test]
805 fn test_escrow_finish_serialization() {
806 let tx = escrow_finish(&ACCOUNT, &DEST, 44, 45, 12);
807 assert!(!tx.is_empty());
808 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_ESCROW_FINISH);
809 }
810
811 #[test]
812 fn test_escrow_cancel_serialization() {
813 let tx = escrow_cancel(&ACCOUNT, &DEST, 44, 46, 12);
814 assert!(!tx.is_empty());
815 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_ESCROW_CANCEL);
816 }
817
818 #[test]
821 fn test_xrp_amount_encoding() {
822 let amt = encode_xrp_amount(1_000_000);
823 assert_eq!(amt[0] & 0x40, 0x40);
824 assert_eq!(amt[0] & 0x80, 0x00);
825 }
826
827 #[test]
830 fn test_account_set_with_flag() {
831 let tx = account_set(
832 &ACCOUNT,
833 Some(account_set_flags::ASF_REQUIRE_DEST),
834 None,
835 1,
836 12,
837 );
838 assert!(!tx.is_empty());
839 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_ACCOUNT_SET);
840 }
841
842 #[test]
843 fn test_account_set_clear_flag() {
844 let tx = account_set(
845 &ACCOUNT,
846 None,
847 Some(account_set_flags::ASF_DISALLOW_XRP),
848 2,
849 12,
850 );
851 assert!(!tx.is_empty());
852 }
853
854 #[test]
855 fn test_account_set_no_flags() {
856 let tx = account_set(&ACCOUNT, None, None, 3, 12);
857 assert!(!tx.is_empty());
858 }
859
860 #[test]
863 fn test_channel_create() {
864 let pk = [0x02; 33];
865 let tx = channel_create(&ACCOUNT, &DEST, 10_000_000, 3600, &pk, 1, 12);
866 assert!(!tx.is_empty());
867 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_CHANNEL_CREATE);
868 }
869
870 #[test]
871 fn test_channel_fund() {
872 let channel = [0xAA; 32];
873 let tx = channel_fund(&ACCOUNT, &channel, 5_000_000, 2, 12);
874 assert!(!tx.is_empty());
875 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_CHANNEL_FUND);
876 }
877
878 #[test]
879 fn test_channel_claim() {
880 let channel = [0xBB; 32];
881 let tx = channel_claim(&ACCOUNT, &channel, Some(1_000_000), 3, 12);
882 assert!(!tx.is_empty());
883 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_CHANNEL_CLAIM);
884 }
885
886 #[test]
889 fn test_nftoken_mint() {
890 let tx = nftoken_mint(
891 &ACCOUNT,
892 0,
893 nftoken_flags::TF_TRANSFERABLE,
894 Some(b"ipfs://QmTest"),
895 1,
896 12,
897 );
898 assert!(!tx.is_empty());
899 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_NFTOKEN_MINT);
900 }
901
902 #[test]
903 fn test_nftoken_mint_no_uri() {
904 let tx1 = nftoken_mint(&ACCOUNT, 1, 0, None, 1, 12);
905 let tx2 = nftoken_mint(&ACCOUNT, 1, 0, Some(b"test"), 1, 12);
906 assert!(tx2.len() > tx1.len());
907 }
908
909 #[test]
910 fn test_nftoken_create_offer() {
911 let nft_id = [0xAA; 32];
912 let tx = nftoken_create_offer(&ACCOUNT, &nft_id, 1_000_000, 0, Some(&DEST), 1, 12);
913 assert!(!tx.is_empty());
914 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_NFTOKEN_CREATE_OFFER);
915 }
916
917 #[test]
918 fn test_nftoken_accept_offer() {
919 let offer = [0xBB; 32];
920 let tx = nftoken_accept_offer(&ACCOUNT, Some(&offer), None, 1, 12);
921 assert!(!tx.is_empty());
922 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_NFTOKEN_ACCEPT_OFFER);
923 }
924
925 #[test]
926 fn test_nftoken_burn() {
927 let nft_id = [0xCC; 32];
928 let tx = nftoken_burn(&ACCOUNT, &nft_id, 1, 12);
929 assert!(!tx.is_empty());
930 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_NFTOKEN_BURN);
931 }
932
933 #[test]
936 fn test_check_create() {
937 let tx = check_create(&ACCOUNT, &DEST, 5_000_000, 1, 12);
938 assert!(!tx.is_empty());
939 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_CHECK_CREATE);
940 }
941
942 #[test]
943 fn test_check_cash() {
944 let check_id = [0xDD; 32];
945 let tx = check_cash(&ACCOUNT, &check_id, 5_000_000, 2, 12);
946 assert!(!tx.is_empty());
947 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_CHECK_CASH);
948 }
949
950 #[test]
951 fn test_check_cancel() {
952 let check_id = [0xEE; 32];
953 let tx = check_cancel(&ACCOUNT, &check_id, 3, 12);
954 assert!(!tx.is_empty());
955 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_CHECK_CANCEL);
956 }
957
958 #[test]
961 fn test_set_hook() {
962 let hook_hash = [0xFF; 32];
963 let tx = set_hook(&ACCOUNT, &hook_hash, 1, 12);
964 assert!(!tx.is_empty());
965 assert_eq!(u16::from_be_bytes([tx[1], tx[2]]), TT_SET_HOOK);
966 }
967}