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/>.
16
use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
17
use scale_info::TypeInfo;
18
use sp_std::prelude::*;
19

            
20
20
#[derive(Debug, PartialEq, Eq, Clone, Copy, Encode, TypeInfo, MaxEncodedLen)]
21
#[scale_info(skip_type_params(LOWER, UPPER))]
22
pub struct BoundedU128<const LOWER: u128, const UPPER: u128>(u128);
23

            
24
impl<const L: u128, const U: u128> BoundedU128<L, U> {
25
	// Create a new instance with a value. If the value is out of bounds, it will return an error.
26
8
	pub fn new(value: u128) -> Result<Self, &'static str> {
27
8
		if value < L || value > U {
28
2
			return Err("Value out of bounds");
29
6
		}
30
6
		Ok(Self(value))
31
8
	}
32

            
33
	/// Create a new instance with a constant value. If the value is out of bounds, it will be
34
	/// clamped to the nearest bound.
35
23
	pub fn const_new<const VAL: u128>() -> Self {
36
23
		if VAL < L {
37
1
			Self(L)
38
22
		} else if VAL > U {
39
1
			Self(U)
40
		} else {
41
21
			Self(VAL)
42
		}
43
23
	}
44

            
45
	/// Get the value.
46
26
	pub fn value(&self) -> u128 {
47
26
		self.0
48
26
	}
49
}
50

            
51
impl<const L: u128, const U: u128> Decode for BoundedU128<L, U> {
52
3
	fn decode<I: parity_scale_codec::Input>(
53
3
		input: &mut I,
54
3
	) -> Result<Self, parity_scale_codec::Error> {
55
3
		let value = u128::decode(input)?;
56
3
		if value < L || value > U {
57
2
			return Err("Value out of bounds".into());
58
1
		}
59
1
		Ok(Self(value))
60
3
	}
61
}
62

            
63
impl<const L: u128, const U: u128> EncodeLike<u128> for BoundedU128<L, U> {}
64

            
65
/// Expose a `Get<u128>` implementation form a `Get<BoundedU128>` type.
66
#[macro_export]
67
macro_rules! expose_u128_get {
68
	($name:ident,$bounded_get:ty) => {
69
		pub struct $name;
70

            
71
		impl sp_core::Get<u128> for $name {
72
21
			fn get() -> u128 {
73
21
				<$bounded_get>::get().value()
74
21
			}
75
		}
76
	};
77
}
78

            
79
#[cfg(test)]
80
mod tests {
81
	use frame_support::parameter_types;
82
	use sp_core::Get;
83

            
84
	use super::*;
85

            
86
	#[test]
87
1
	fn test_bounded_u128() {
88
1
		let bounded = BoundedU128::<1, 10>::new(5).unwrap();
89
1
		assert_eq!(bounded.value(), 5);
90

            
91
1
		let bounded = BoundedU128::<1, 10>::new(0);
92
1
		assert_eq!(bounded, Err("Value out of bounds"));
93

            
94
1
		let bounded = BoundedU128::<1, 10>::new(11);
95
1
		assert_eq!(bounded, Err("Value out of bounds"));
96

            
97
1
		let bounded = BoundedU128::<1, 10>::const_new::<0>();
98
1
		assert_eq!(bounded.value(), 1);
99

            
100
1
		let bounded = BoundedU128::<1, 10>::const_new::<5>();
101
1
		assert_eq!(bounded.value(), 5);
102

            
103
1
		let bounded = BoundedU128::<1, 10>::const_new::<11>();
104
1
		assert_eq!(bounded.value(), 10);
105
1
	}
106

            
107
	#[test]
108
1
	fn test_expose_u128_get() {
109
1
		parameter_types! {
110
1
			pub Bounded: BoundedU128::<1, 10> = BoundedU128::<1, 10>::new(4).unwrap();
111
1
		}
112
1
		expose_u128_get!(Exposed, Bounded);
113
1
		assert_eq!(Bounded::get().value(), Exposed::get());
114
1
	}
115

            
116
	#[test]
117
1
	fn test_encode_decode() {
118
1
		let bounded = BoundedU128::<1, 10>::new(5).unwrap();
119
1
		let encoded = bounded.encode();
120
1
		let decoded = BoundedU128::<1, 10>::decode(&mut &encoded[..]).unwrap();
121
1
		assert_eq!(bounded, decoded);
122
1
	}
123

            
124
	#[test]
125
1
	fn test_encode_invalid() {
126
1
		let bounded = BoundedU128::<1, 10>::new(9);
127
1
		let encoded = bounded.encode();
128
1
		let decoded = BoundedU128::<1, 3>::decode(&mut &encoded[..]);
129
1
		assert_eq!(decoded, Err("Value out of bounds".into()));
130

            
131
1
		let bounded = BoundedU128::<1, 10>::new(9);
132
1
		let encoded = bounded.encode();
133
1
		let decoded = BoundedU128::<100, 500>::decode(&mut &encoded[..]);
134
1
		assert_eq!(decoded, Err("Value out of bounds".into()));
135
1
	}
136
}