moonbeam_runtime_common/
types.rs1use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
17use scale_info::TypeInfo;
18use sp_std::prelude::*;
19
20#[derive(
21 Debug, PartialEq, Eq, Clone, Copy, Encode, TypeInfo, MaxEncodedLen, DecodeWithMemTracking,
22)]
23#[scale_info(skip_type_params(LOWER, UPPER))]
24pub struct BoundedU128<const LOWER: u128, const UPPER: u128>(u128);
25
26impl<const L: u128, const U: u128> BoundedU128<L, U> {
27 pub fn new(value: u128) -> Result<Self, &'static str> {
29 if value < L || value > U {
30 return Err("Value out of bounds");
31 }
32 Ok(Self(value))
33 }
34
35 pub fn const_new<const VAL: u128>() -> Self {
38 if VAL < L {
39 Self(L)
40 } else if VAL > U {
41 Self(U)
42 } else {
43 Self(VAL)
44 }
45 }
46
47 pub fn value(&self) -> u128 {
49 self.0
50 }
51}
52
53impl<const L: u128, const U: u128> Decode for BoundedU128<L, U> {
54 fn decode<I: parity_scale_codec::Input>(
55 input: &mut I,
56 ) -> Result<Self, parity_scale_codec::Error> {
57 let value = u128::decode(input)?;
58 if value < L || value > U {
59 return Err("Value out of bounds".into());
60 }
61 Ok(Self(value))
62 }
63}
64
65impl<const L: u128, const U: u128> EncodeLike<u128> for BoundedU128<L, U> {}
66
67#[macro_export]
69macro_rules! expose_u128_get {
70 ($name:ident,$bounded_get:ty) => {
71 pub struct $name;
72
73 impl sp_core::Get<u128> for $name {
74 fn get() -> u128 {
75 <$bounded_get>::get().value()
76 }
77 }
78 };
79}
80
81#[cfg(test)]
82mod tests {
83 use frame_support::parameter_types;
84 use sp_core::Get;
85
86 use super::*;
87
88 #[test]
89 fn test_bounded_u128() {
90 let bounded = BoundedU128::<1, 10>::new(5).unwrap();
91 assert_eq!(bounded.value(), 5);
92
93 let bounded = BoundedU128::<1, 10>::new(0);
94 assert_eq!(bounded, Err("Value out of bounds"));
95
96 let bounded = BoundedU128::<1, 10>::new(11);
97 assert_eq!(bounded, Err("Value out of bounds"));
98
99 let bounded = BoundedU128::<1, 10>::const_new::<0>();
100 assert_eq!(bounded.value(), 1);
101
102 let bounded = BoundedU128::<1, 10>::const_new::<5>();
103 assert_eq!(bounded.value(), 5);
104
105 let bounded = BoundedU128::<1, 10>::const_new::<11>();
106 assert_eq!(bounded.value(), 10);
107 }
108
109 #[test]
110 fn test_expose_u128_get() {
111 parameter_types! {
112 pub Bounded: BoundedU128::<1, 10> = BoundedU128::<1, 10>::new(4).unwrap();
113 }
114 expose_u128_get!(Exposed, Bounded);
115 assert_eq!(Bounded::get().value(), Exposed::get());
116 }
117
118 #[test]
119 fn test_encode_decode() {
120 let bounded = BoundedU128::<1, 10>::new(5).unwrap();
121 let encoded = bounded.encode();
122 let decoded = BoundedU128::<1, 10>::decode(&mut &encoded[..]).unwrap();
123 assert_eq!(bounded, decoded);
124 }
125
126 #[test]
127 fn test_encode_invalid() {
128 let bounded = BoundedU128::<1, 10>::new(9);
129 let encoded = bounded.encode();
130 let decoded = BoundedU128::<1, 3>::decode(&mut &encoded[..]);
131 assert_eq!(decoded, Err("Value out of bounds".into()));
132
133 let bounded = BoundedU128::<1, 10>::new(9);
134 let encoded = bounded.encode();
135 let decoded = BoundedU128::<100, 500>::decode(&mut &encoded[..]);
136 assert_eq!(decoded, Err("Value out of bounds".into()));
137 }
138}