1use alloc::vec::Vec;
4use core::fmt;
5
6#[cfg(feature = "datasize")]
7use datasize::DataSize;
8#[cfg(any(feature = "testing", test))]
9use rand::Rng;
10#[cfg(feature = "json-schema")]
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13
14#[cfg(any(feature = "testing", test))]
15use crate::testing::TestRng;
16use crate::{
17 bytesrepr::{self, FromBytes, ToBytes},
18 Motes, U512,
19};
20
21#[derive(
23 Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
24)]
25#[cfg_attr(feature = "datasize", derive(DataSize))]
26#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
27pub struct Gas(U512);
28
29impl Gas {
30 pub const MAX: Gas = Gas(U512::MAX);
32
33 pub fn new<T: Into<U512>>(value: T) -> Self {
35 Gas(value.into())
36 }
37
38 pub const fn zero() -> Self {
40 Gas(U512::zero())
41 }
42
43 pub fn value(&self) -> U512 {
45 self.0
46 }
47
48 pub fn from_motes(motes: Motes, motes_per_unit_of_gas: u8) -> Option<Self> {
52 motes
53 .value()
54 .checked_div(U512::from(motes_per_unit_of_gas))
55 .map(Self::new)
56 }
57
58 pub fn from_price(base_amount: U512, gas_price: u8) -> Option<Self> {
62 base_amount
63 .checked_div(U512::from(gas_price))
64 .map(Self::new)
65 }
66
67 pub fn checked_add(&self, rhs: Self) -> Option<Self> {
69 self.0.checked_add(rhs.value()).map(Self::new)
70 }
71
72 pub fn saturating_add(self, rhs: Self) -> Self {
74 Gas(self.0.saturating_add(rhs.value()))
75 }
76
77 pub fn saturating_sub(self, rhs: Self) -> Self {
79 Gas(self.0.saturating_sub(rhs.value()))
80 }
81
82 pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
84 self.0.checked_sub(rhs.value()).map(Self::new)
85 }
86
87 pub fn checked_mul(&self, rhs: Self) -> Option<Self> {
89 self.0.checked_mul(rhs.value()).map(Self::new)
90 }
91
92 pub fn checked_div(&self, rhs: Self) -> Option<Self> {
94 self.0.checked_div(rhs.value()).map(Self::new)
95 }
96
97 #[cfg(any(feature = "testing", test))]
99 pub fn random(rng: &mut TestRng) -> Self {
100 Self(rng.gen::<u128>().into())
101 }
102}
103
104impl ToBytes for Gas {
105 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
106 self.0.to_bytes()
107 }
108
109 fn serialized_length(&self) -> usize {
110 self.0.serialized_length()
111 }
112
113 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
114 self.0.write_bytes(writer)
115 }
116}
117
118impl FromBytes for Gas {
119 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
120 let (value, remainder) = U512::from_bytes(bytes)?;
121 Ok((Gas(value), remainder))
122 }
123}
124
125impl fmt::Display for Gas {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 write!(f, "{:?}", self.0)
128 }
129}
130
131impl From<u32> for Gas {
132 fn from(gas: u32) -> Self {
133 let gas_u512: U512 = gas.into();
134 Gas::new(gas_u512)
135 }
136}
137
138impl From<u64> for Gas {
139 fn from(gas: u64) -> Self {
140 let gas_u512: U512 = gas.into();
141 Gas::new(gas_u512)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use crate::U512;
148
149 use crate::{Gas, Motes};
150
151 #[test]
152 fn should_be_able_to_get_instance_of_gas() {
153 let initial_value = 1;
154 let gas = Gas::new(U512::from(initial_value));
155 assert_eq!(
156 initial_value,
157 gas.value().as_u64(),
158 "should have equal value"
159 )
160 }
161
162 #[test]
163 fn should_be_able_to_compare_two_instances_of_gas() {
164 let left_gas = Gas::new(U512::from(1));
165 let right_gas = Gas::new(U512::from(1));
166 assert_eq!(left_gas, right_gas, "should be equal");
167 let right_gas = Gas::new(U512::from(2));
168 assert_ne!(left_gas, right_gas, "should not be equal")
169 }
170
171 #[test]
172 fn should_be_able_to_add_two_instances_of_gas() {
173 let left_gas = Gas::new(U512::from(1));
174 let right_gas = Gas::new(U512::from(1));
175 let expected_gas = Gas::new(U512::from(2));
176 assert_eq!(
177 left_gas.checked_add(right_gas),
178 Some(expected_gas),
179 "should be equal"
180 )
181 }
182
183 #[test]
184 fn should_be_able_to_subtract_two_instances_of_gas() {
185 let left_gas = Gas::new(U512::from(1));
186 let right_gas = Gas::new(U512::from(1));
187 let expected_gas = Gas::new(U512::from(0));
188 assert_eq!(
189 left_gas.checked_sub(right_gas),
190 Some(expected_gas),
191 "should be equal"
192 )
193 }
194
195 #[test]
196 fn should_be_able_to_multiply_two_instances_of_gas() {
197 let left_gas = Gas::new(U512::from(100));
198 let right_gas = Gas::new(U512::from(10));
199 let expected_gas = Gas::new(U512::from(1000));
200 assert_eq!(
201 left_gas.checked_mul(right_gas),
202 Some(expected_gas),
203 "should be equal"
204 )
205 }
206
207 #[test]
208 fn should_be_able_to_divide_two_instances_of_gas() {
209 let left_gas = Gas::new(U512::from(1000));
210 let right_gas = Gas::new(U512::from(100));
211 let expected_gas = Gas::new(U512::from(10));
212 assert_eq!(
213 left_gas.checked_div(right_gas),
214 Some(expected_gas),
215 "should be equal"
216 )
217 }
218
219 #[test]
220 fn should_be_able_to_convert_from_mote() {
221 let mote = Motes::new(U512::from(100));
222 let gas = Gas::from_motes(mote, 10).expect("should have gas");
223 let expected_gas = Gas::new(U512::from(10));
224 assert_eq!(gas, expected_gas, "should be equal")
225 }
226
227 #[test]
228 fn should_be_able_to_default() {
229 let gas = Gas::default();
230 let expected_gas = Gas::zero();
231 assert_eq!(gas, expected_gas, "should be equal")
232 }
233
234 #[test]
235 fn should_be_able_to_compare_relative_value() {
236 let left_gas = Gas::new(U512::from(100));
237 let right_gas = Gas::new(U512::from(10));
238 assert!(left_gas > right_gas, "should be gt");
239 let right_gas = Gas::new(U512::from(100));
240 assert!(left_gas >= right_gas, "should be gte");
241 assert!(left_gas <= right_gas, "should be lte");
242 let left_gas = Gas::new(U512::from(10));
243 assert!(left_gas < right_gas, "should be lt");
244 }
245
246 #[test]
247 fn should_support_checked_div_from_motes() {
248 let motes = Motes::zero();
249 let conv_rate = 0;
250 let maybe = Gas::from_motes(motes, conv_rate);
251 assert!(maybe.is_none(), "should be none due to divide by zero");
252 }
253}