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, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
17
use scale_info::TypeInfo;
18
use sp_std::prelude::*;
19

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

            
26
impl<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
8
	pub fn new(value: u128) -> Result<Self, &'static str> {
29
8
		if value < L || value > U {
30
2
			return Err("Value out of bounds");
31
6
		}
32
6
		Ok(Self(value))
33
8
	}
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
23
	pub fn const_new<const VAL: u128>() -> Self {
38
23
		if VAL < L {
39
1
			Self(L)
40
22
		} else if VAL > U {
41
1
			Self(U)
42
		} else {
43
21
			Self(VAL)
44
		}
45
23
	}
46

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

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

            
65
impl<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]
69
macro_rules! expose_u128_get {
70
	($name:ident,$bounded_get:ty) => {
71
		pub struct $name;
72

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

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

            
86
	use super::*;
87

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

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

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

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

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

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

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

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

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

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