moonbeam_runtime_common/
types.rs

1// Copyright 2025 Moonbeam Foundation.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16use 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	// Create a new instance with a value. If the value is out of bounds, it will return an error.
28	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	/// Create a new instance with a constant value. If the value is out of bounds, it will be
36	/// clamped to the nearest bound.
37	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	/// Get the value.
48	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/// Expose a `Get<u128>` implementation form a `Get<BoundedU128>` type.
68#[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}