Skip to main content

hopper_native/
wire.rs

1//! Alignment-safe wire types for zero-copy account data.
2//!
3//! Account data APIs expose byte buffers without guaranteeing native-integer
4//! alignment. Forming a `u64` reference at an unaligned address is undefined
5//! behavior. Hopper's wire integers store little-endian bytes at alignment 1:
6//!
7//! - **Explicit endianness**: Types are named `LeU64` ("little-endian u64"),
8//!   making the wire representation explicit at call sites.
9//! - **Explicit arithmetic semantics**: the `checked_*`, `saturating_*`,
10//!   and `wrapping_*` inherent methods spell out overflow behavior at the
11//!   call site. The `+`/`-`/`*` operators mirror Rust's native integers
12//!   (panic on overflow in debug, wrap in release). Prefer the explicit
13//!   methods for on-chain balance math.
14//! - **`const fn` constructors**: `LeU64::new(42)` works in const context,
15//!   enabling compile-time constants for discriminators, seeds, etc.
16//! - **`Pod` + `Projectable`**: All wire types satisfy both the substrate
17//!   [`crate::Pod`] overlay contract and [`Projectable`], so
18//!   `lens::read_field_pod::<LeU64>` and `project::<LeU64>` both work
19//!   directly on account data without alignment issues.
20//!
21//! A `#[repr(C)]` struct composed entirely of these wire types and alignment-1
22//! byte arrays can satisfy Hopper's zero-copy overlay contract.
23
24use crate::project::Projectable;
25
26// ---- Macro to generate integer wire types ----------------------------
27
28macro_rules! le_integer {
29    (
30        $(#[$meta:meta])*
31        $name:ident, $native:ty, $size:expr, unsigned
32    ) => {
33        $(#[$meta])*
34        #[repr(transparent)]
35        #[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
36        pub struct $name([u8; $size]);
37
38        impl $name {
39            /// Zero value.
40            pub const ZERO: Self = Self([0; $size]);
41
42            /// Maximum representable value.
43            pub const MAX: Self = Self(<$native>::MAX.to_le_bytes());
44
45            /// Construct from a native integer (const-safe).
46            #[inline(always)]
47            pub const fn new(v: $native) -> Self {
48                Self(v.to_le_bytes())
49            }
50
51            /// Read the native integer value.
52            #[inline(always)]
53            pub const fn get(self) -> $native {
54                <$native>::from_le_bytes(self.0)
55            }
56
57            /// Raw little-endian bytes.
58            #[inline(always)]
59            pub const fn to_le_bytes(self) -> [u8; $size] {
60                self.0
61            }
62
63            /// Construct from raw little-endian bytes.
64            #[inline(always)]
65            pub const fn from_le_bytes(bytes: [u8; $size]) -> Self {
66                Self(bytes)
67            }
68
69            /// Checked addition. Returns `None` on overflow.
70            #[inline(always)]
71            pub const fn checked_add(self, rhs: Self) -> Option<Self> {
72                match self.get().checked_add(rhs.get()) {
73                    Some(v) => Some(Self::new(v)),
74                    None => None,
75                }
76            }
77
78            /// Checked subtraction. Returns `None` on underflow.
79            #[inline(always)]
80            pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
81                match self.get().checked_sub(rhs.get()) {
82                    Some(v) => Some(Self::new(v)),
83                    None => None,
84                }
85            }
86
87            /// Checked multiplication. Returns `None` on overflow.
88            #[inline(always)]
89            pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
90                match self.get().checked_mul(rhs.get()) {
91                    Some(v) => Some(Self::new(v)),
92                    None => None,
93                }
94            }
95
96            /// Checked division. Returns `None` on divide-by-zero.
97            #[inline(always)]
98            pub const fn checked_div(self, rhs: Self) -> Option<Self> {
99                match self.get().checked_div(rhs.get()) {
100                    Some(v) => Some(Self::new(v)),
101                    None => None,
102                }
103            }
104
105            /// Saturating addition (clamps at MAX instead of wrapping).
106            #[inline(always)]
107            pub const fn saturating_add(self, rhs: Self) -> Self {
108                Self::new(self.get().saturating_add(rhs.get()))
109            }
110
111            /// Saturating subtraction (clamps at 0 instead of wrapping).
112            #[inline(always)]
113            pub const fn saturating_sub(self, rhs: Self) -> Self {
114                Self::new(self.get().saturating_sub(rhs.get()))
115            }
116
117            /// Wrapping addition (use explicitly when wrapping is intended).
118            #[inline(always)]
119            pub const fn wrapping_add(self, rhs: Self) -> Self {
120                Self::new(self.get().wrapping_add(rhs.get()))
121            }
122
123            /// Wrapping subtraction.
124            #[inline(always)]
125            pub const fn wrapping_sub(self, rhs: Self) -> Self {
126                Self::new(self.get().wrapping_sub(rhs.get()))
127            }
128
129            /// Whether the value is zero.
130            #[inline(always)]
131            pub const fn is_zero(self) -> bool {
132                self.get() == 0
133            }
134        }
135
136        impl From<$native> for $name {
137            #[inline(always)]
138            fn from(v: $native) -> Self { Self::new(v) }
139        }
140
141        impl From<$name> for $native {
142            #[inline(always)]
143            fn from(v: $name) -> Self { v.get() }
144        }
145
146        impl PartialOrd for $name {
147            #[inline(always)]
148            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
149                Some(self.cmp(other))
150            }
151        }
152
153        impl Ord for $name {
154            #[inline(always)]
155            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
156                self.get().cmp(&other.get())
157            }
158        }
159
160        impl core::fmt::Debug for $name {
161            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162                write!(f, "{}({})", stringify!($name), self.get())
163            }
164        }
165
166        impl core::fmt::Display for $name {
167            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
168                write!(f, "{}", self.get())
169            }
170        }
171
172        // SAFETY: $name is #[repr(transparent)] over [u8; N].
173        // All bit patterns are valid (no padding, no alignment requirement).
174        unsafe impl Projectable for $name {}
175        // SAFETY: #[repr(transparent)] over [u8; N]: alignment 1, no padding,
176        // every bit pattern valid, no internal pointers, the full substrate
177        // Pod overlay contract.
178        unsafe impl $crate::pod::Zeroable for $name {}
179        unsafe impl $crate::pod::Pod for $name {}
180
181        $crate::__wire_arith_ops!($name, $native);
182    };
183
184    // Signed variant -- same API but with signed native type.
185    (
186        $(#[$meta:meta])*
187        $name:ident, $native:ty, $size:expr, signed
188    ) => {
189        $(#[$meta])*
190        #[repr(transparent)]
191        #[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
192        pub struct $name([u8; $size]);
193
194        impl $name {
195            /// Zero value.
196            pub const ZERO: Self = Self([0; $size]);
197
198            /// Maximum representable value.
199            pub const MAX: Self = Self(<$native>::MAX.to_le_bytes());
200
201            /// Minimum representable value.
202            pub const MIN: Self = Self(<$native>::MIN.to_le_bytes());
203
204            /// Construct from a native integer (const-safe).
205            #[inline(always)]
206            pub const fn new(v: $native) -> Self {
207                Self(v.to_le_bytes())
208            }
209
210            /// Read the native integer value.
211            #[inline(always)]
212            pub const fn get(self) -> $native {
213                <$native>::from_le_bytes(self.0)
214            }
215
216            /// Raw little-endian bytes.
217            #[inline(always)]
218            pub const fn to_le_bytes(self) -> [u8; $size] {
219                self.0
220            }
221
222            /// Construct from raw little-endian bytes.
223            #[inline(always)]
224            pub const fn from_le_bytes(bytes: [u8; $size]) -> Self {
225                Self(bytes)
226            }
227
228            /// Checked addition.
229            #[inline(always)]
230            pub const fn checked_add(self, rhs: Self) -> Option<Self> {
231                match self.get().checked_add(rhs.get()) {
232                    Some(v) => Some(Self::new(v)),
233                    None => None,
234                }
235            }
236
237            /// Checked subtraction.
238            #[inline(always)]
239            pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
240                match self.get().checked_sub(rhs.get()) {
241                    Some(v) => Some(Self::new(v)),
242                    None => None,
243                }
244            }
245
246            /// Checked multiplication.
247            #[inline(always)]
248            pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
249                match self.get().checked_mul(rhs.get()) {
250                    Some(v) => Some(Self::new(v)),
251                    None => None,
252                }
253            }
254
255            /// Checked division.
256            #[inline(always)]
257            pub const fn checked_div(self, rhs: Self) -> Option<Self> {
258                match self.get().checked_div(rhs.get()) {
259                    Some(v) => Some(Self::new(v)),
260                    None => None,
261                }
262            }
263
264            /// Saturating addition.
265            #[inline(always)]
266            pub const fn saturating_add(self, rhs: Self) -> Self {
267                Self::new(self.get().saturating_add(rhs.get()))
268            }
269
270            /// Saturating subtraction.
271            #[inline(always)]
272            pub const fn saturating_sub(self, rhs: Self) -> Self {
273                Self::new(self.get().saturating_sub(rhs.get()))
274            }
275
276            /// Whether the value is zero.
277            #[inline(always)]
278            pub const fn is_zero(self) -> bool {
279                self.get() == 0
280            }
281
282            /// Whether the value is negative.
283            #[inline(always)]
284            pub const fn is_negative(self) -> bool {
285                self.get() < 0
286            }
287
288            /// Absolute value (wraps on MIN).
289            #[inline(always)]
290            pub const fn abs(self) -> Self {
291                Self::new(self.get().wrapping_abs())
292            }
293        }
294
295        impl From<$native> for $name {
296            #[inline(always)]
297            fn from(v: $native) -> Self { Self::new(v) }
298        }
299
300        impl From<$name> for $native {
301            #[inline(always)]
302            fn from(v: $name) -> Self { v.get() }
303        }
304
305        impl PartialOrd for $name {
306            #[inline(always)]
307            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
308                Some(self.cmp(other))
309            }
310        }
311
312        impl Ord for $name {
313            #[inline(always)]
314            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
315                self.get().cmp(&other.get())
316            }
317        }
318
319        impl core::fmt::Debug for $name {
320            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
321                write!(f, "{}({})", stringify!($name), self.get())
322            }
323        }
324
325        impl core::fmt::Display for $name {
326            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
327                write!(f, "{}", self.get())
328            }
329        }
330
331        unsafe impl Projectable for $name {}
332        // SAFETY: #[repr(transparent)] over [u8; N]: alignment 1, no padding,
333        // every bit pattern valid, no internal pointers.
334        unsafe impl $crate::pod::Zeroable for $name {}
335        unsafe impl $crate::pod::Pod for $name {}
336
337        $crate::__wire_arith_ops!($name, $native);
338    };
339}
340
341/// Internal: emit arithmetic operator impls for a wire integer type.
342///
343/// Mirrors Rust's native integer behavior: panic on overflow in debug,
344/// wrap in release. Programs that need explicit semantics should use the
345/// `checked_*`, `saturating_*`, or `wrapping_*` inherent methods.
346#[doc(hidden)]
347#[macro_export]
348macro_rules! __wire_arith_ops {
349    ($name:ident, $native:ty) => {
350        impl core::ops::Add for $name {
351            type Output = Self;
352            #[inline(always)]
353            fn add(self, rhs: Self) -> Self {
354                Self::new(self.get() + rhs.get())
355            }
356        }
357        impl core::ops::Sub for $name {
358            type Output = Self;
359            #[inline(always)]
360            fn sub(self, rhs: Self) -> Self {
361                Self::new(self.get() - rhs.get())
362            }
363        }
364        impl core::ops::Mul for $name {
365            type Output = Self;
366            #[inline(always)]
367            fn mul(self, rhs: Self) -> Self {
368                Self::new(self.get() * rhs.get())
369            }
370        }
371        impl core::ops::Div for $name {
372            type Output = Self;
373            #[inline(always)]
374            fn div(self, rhs: Self) -> Self {
375                Self::new(self.get() / rhs.get())
376            }
377        }
378        impl core::ops::Rem for $name {
379            type Output = Self;
380            #[inline(always)]
381            fn rem(self, rhs: Self) -> Self {
382                Self::new(self.get() % rhs.get())
383            }
384        }
385        impl core::ops::Add<$native> for $name {
386            type Output = Self;
387            #[inline(always)]
388            fn add(self, rhs: $native) -> Self {
389                Self::new(self.get() + rhs)
390            }
391        }
392        impl core::ops::Sub<$native> for $name {
393            type Output = Self;
394            #[inline(always)]
395            fn sub(self, rhs: $native) -> Self {
396                Self::new(self.get() - rhs)
397            }
398        }
399        impl core::ops::Mul<$native> for $name {
400            type Output = Self;
401            #[inline(always)]
402            fn mul(self, rhs: $native) -> Self {
403                Self::new(self.get() * rhs)
404            }
405        }
406        impl core::ops::Div<$native> for $name {
407            type Output = Self;
408            #[inline(always)]
409            fn div(self, rhs: $native) -> Self {
410                Self::new(self.get() / rhs)
411            }
412        }
413        impl core::ops::Rem<$native> for $name {
414            type Output = Self;
415            #[inline(always)]
416            fn rem(self, rhs: $native) -> Self {
417                Self::new(self.get() % rhs)
418            }
419        }
420        impl core::ops::AddAssign for $name {
421            #[inline(always)]
422            fn add_assign(&mut self, rhs: Self) {
423                *self = *self + rhs;
424            }
425        }
426        impl core::ops::SubAssign for $name {
427            #[inline(always)]
428            fn sub_assign(&mut self, rhs: Self) {
429                *self = *self - rhs;
430            }
431        }
432        impl core::ops::MulAssign for $name {
433            #[inline(always)]
434            fn mul_assign(&mut self, rhs: Self) {
435                *self = *self * rhs;
436            }
437        }
438        impl core::ops::DivAssign for $name {
439            #[inline(always)]
440            fn div_assign(&mut self, rhs: Self) {
441                *self = *self / rhs;
442            }
443        }
444        impl core::ops::RemAssign for $name {
445            #[inline(always)]
446            fn rem_assign(&mut self, rhs: Self) {
447                *self = *self % rhs;
448            }
449        }
450        impl core::ops::AddAssign<$native> for $name {
451            #[inline(always)]
452            fn add_assign(&mut self, rhs: $native) {
453                *self = *self + rhs;
454            }
455        }
456        impl core::ops::SubAssign<$native> for $name {
457            #[inline(always)]
458            fn sub_assign(&mut self, rhs: $native) {
459                *self = *self - rhs;
460            }
461        }
462        impl core::ops::MulAssign<$native> for $name {
463            #[inline(always)]
464            fn mul_assign(&mut self, rhs: $native) {
465                *self = *self * rhs;
466            }
467        }
468        impl core::ops::DivAssign<$native> for $name {
469            #[inline(always)]
470            fn div_assign(&mut self, rhs: $native) {
471                *self = *self / rhs;
472            }
473        }
474        impl core::ops::RemAssign<$native> for $name {
475            #[inline(always)]
476            fn rem_assign(&mut self, rhs: $native) {
477                *self = *self % rhs;
478            }
479        }
480        impl PartialEq<$native> for $name {
481            #[inline(always)]
482            fn eq(&self, other: &$native) -> bool {
483                self.get() == *other
484            }
485        }
486        impl PartialOrd<$native> for $name {
487            #[inline(always)]
488            fn partial_cmp(&self, other: &$native) -> Option<core::cmp::Ordering> {
489                Some(self.get().cmp(other))
490            }
491        }
492    };
493}
494
495// ---- Unsigned wire types ---------------------------------------------
496
497le_integer! {
498    /// 64-bit unsigned little-endian integer. Alignment 1.
499    ///
500    /// The workhorse type for token amounts, lamport balances, timestamps,
501    /// and most on-chain numeric fields. Use this instead of `u64` in any
502    /// `#[repr(C)]` struct that will be projected from account data.
503    LeU64, u64, 8, unsigned
504}
505
506le_integer! {
507    /// 32-bit unsigned little-endian integer. Alignment 1.
508    LeU32, u32, 4, unsigned
509}
510
511le_integer! {
512    /// 16-bit unsigned little-endian integer. Alignment 1.
513    LeU16, u16, 2, unsigned
514}
515
516// ---- Signed wire types -----------------------------------------------
517
518le_integer! {
519    /// 64-bit signed little-endian integer. Alignment 1.
520    ///
521    /// Used for timestamps (unix_timestamp is i64), deltas, and any
522    /// signed arithmetic in account data.
523    LeI64, i64, 8, signed
524}
525
526le_integer! {
527    /// 32-bit signed little-endian integer. Alignment 1.
528    LeI32, i32, 4, signed
529}
530
531le_integer! {
532    /// 16-bit signed little-endian integer. Alignment 1.
533    LeI16, i16, 2, signed
534}
535
536// ---- LeBool ----------------------------------------------------------
537
538/// Boolean wire type. Alignment 1.
539///
540/// Stored as a single byte: 0 = false, nonzero = true.
541/// [`LeBool::is_canonical`] returns true only for 0 or 1, which lets callers
542/// reject non-canonical encodings.
543#[repr(transparent)]
544#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
545pub struct LeBool(u8);
546
547impl LeBool {
548    /// Canonical true value.
549    pub const TRUE: Self = Self(1);
550
551    /// Canonical false value.
552    pub const FALSE: Self = Self(0);
553
554    /// Construct from a Rust bool.
555    #[inline(always)]
556    pub const fn new(v: bool) -> Self {
557        Self(v as u8)
558    }
559
560    /// Read as a Rust bool (0 = false, anything else = true).
561    #[inline(always)]
562    pub const fn get(self) -> bool {
563        self.0 != 0
564    }
565
566    /// Raw byte value.
567    #[inline(always)]
568    pub const fn raw(self) -> u8 {
569        self.0
570    }
571
572    /// Whether the byte is strictly 0 or 1 (canonical representation).
573    ///
574    /// Non-canonical values (2..=255) are technically "true" but may
575    /// indicate data corruption or an incompatible writer.
576    #[inline(always)]
577    pub const fn is_canonical(self) -> bool {
578        self.0 == 0 || self.0 == 1
579    }
580}
581
582impl From<bool> for LeBool {
583    #[inline(always)]
584    fn from(v: bool) -> Self {
585        Self::new(v)
586    }
587}
588
589impl From<LeBool> for bool {
590    #[inline(always)]
591    fn from(v: LeBool) -> Self {
592        v.get()
593    }
594}
595
596impl core::fmt::Debug for LeBool {
597    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
598        write!(f, "LeBool({})", self.get())
599    }
600}
601
602impl core::fmt::Display for LeBool {
603    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
604        write!(f, "{}", self.get())
605    }
606}
607
608// SAFETY: LeBool is #[repr(transparent)] over u8. All bit patterns valid
609// (`get()` treats any nonzero byte as true; `is_canonical()` flags 2..=255).
610unsafe impl Projectable for LeBool {}
611// SAFETY: as above, alignment 1, no padding, every bit pattern valid.
612unsafe impl crate::pod::Zeroable for LeBool {}
613unsafe impl crate::pod::Pod for LeBool {}
614
615// ---- LeU128 ----------------------------------------------------------
616
617/// 128-bit unsigned little-endian integer. Alignment 1.
618///
619/// Useful for large amounts (e.g., total supply tracking) where u64
620/// would overflow. Stored as 16 bytes in account data.
621#[repr(transparent)]
622#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
623pub struct LeU128([u8; 16]);
624
625impl LeU128 {
626    pub const ZERO: Self = Self([0; 16]);
627    pub const MAX: Self = Self(u128::MAX.to_le_bytes());
628
629    #[inline(always)]
630    pub const fn new(v: u128) -> Self {
631        Self(v.to_le_bytes())
632    }
633
634    #[inline(always)]
635    pub const fn get(self) -> u128 {
636        u128::from_le_bytes(self.0)
637    }
638
639    #[inline(always)]
640    pub const fn to_le_bytes(self) -> [u8; 16] {
641        self.0
642    }
643
644    #[inline(always)]
645    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
646        match self.get().checked_add(rhs.get()) {
647            Some(v) => Some(Self::new(v)),
648            None => None,
649        }
650    }
651
652    #[inline(always)]
653    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
654        match self.get().checked_sub(rhs.get()) {
655            Some(v) => Some(Self::new(v)),
656            None => None,
657        }
658    }
659
660    #[inline(always)]
661    pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
662        match self.get().checked_mul(rhs.get()) {
663            Some(v) => Some(Self::new(v)),
664            None => None,
665        }
666    }
667
668    #[inline(always)]
669    pub const fn saturating_add(self, rhs: Self) -> Self {
670        Self::new(self.get().saturating_add(rhs.get()))
671    }
672
673    #[inline(always)]
674    pub const fn saturating_sub(self, rhs: Self) -> Self {
675        Self::new(self.get().saturating_sub(rhs.get()))
676    }
677
678    #[inline(always)]
679    pub const fn is_zero(self) -> bool {
680        self.get() == 0
681    }
682}
683
684impl From<u128> for LeU128 {
685    #[inline(always)]
686    fn from(v: u128) -> Self {
687        Self::new(v)
688    }
689}
690
691impl From<LeU128> for u128 {
692    #[inline(always)]
693    fn from(v: LeU128) -> Self {
694        v.get()
695    }
696}
697
698impl PartialOrd for LeU128 {
699    #[inline(always)]
700    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
701        Some(self.cmp(other))
702    }
703}
704
705impl Ord for LeU128 {
706    #[inline(always)]
707    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
708        self.get().cmp(&other.get())
709    }
710}
711
712impl core::fmt::Debug for LeU128 {
713    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
714        write!(f, "LeU128({})", self.get())
715    }
716}
717
718impl core::fmt::Display for LeU128 {
719    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
720        write!(f, "{}", self.get())
721    }
722}
723
724unsafe impl Projectable for LeU128 {}
725// SAFETY: #[repr(transparent)] over [u8; 16]: alignment 1, no padding,
726// every bit pattern valid, no internal pointers.
727unsafe impl crate::pod::Zeroable for LeU128 {}
728unsafe impl crate::pod::Pod for LeU128 {}
729
730__wire_arith_ops!(LeU128, u128);
731
732#[cfg(test)]
733mod tests {
734    use super::*;
735
736    fn require_pod<T: crate::pod::Pod>() {}
737
738    /// Every wire type must satisfy the substrate `Pod` overlay contract so
739    /// the Pod-bounded APIs (`lens::read_field_pod`, `segment_ref`) accept
740    /// the crate's own alignment-1 types.
741    #[test]
742    fn wire_types_satisfy_substrate_pod() {
743        require_pod::<LeU64>();
744        require_pod::<LeU32>();
745        require_pod::<LeU16>();
746        require_pod::<LeI64>();
747        require_pod::<LeI32>();
748        require_pod::<LeI16>();
749        require_pod::<LeBool>();
750        require_pod::<LeU128>();
751    }
752
753    #[test]
754    fn wire_roundtrip_and_checked_math() {
755        let a = LeU64::new(u64::MAX - 1);
756        assert_eq!(a.get(), u64::MAX - 1);
757        assert_eq!(a.checked_add(LeU64::new(1)), Some(LeU64::MAX));
758        assert_eq!(LeU64::MAX.checked_add(LeU64::new(1)), None);
759        assert_eq!(LeU64::ZERO.checked_sub(LeU64::new(1)), None);
760        assert!(LeBool::new(true).get());
761        assert!(!LeBool::FALSE.get());
762        assert!(LeBool::TRUE.is_canonical());
763    }
764}