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