1use candid::{CandidType, Nat, types::Serializer, types::Type, types::TypeInner};
4use ethnum::U256 as EthU256;
5use num_bigint::BigUint;
6use serde::{Deserialize, Deserializer, Serialize, Serializer as SerdeSerializer};
7use std::{fmt, str::FromStr};
8
9use crate::{
10 Decimal, NumericValue,
11 integer_wire::{self, IntegerWire},
12};
13
14const MAX_DECIMAL_DIGITS: usize = 78;
15const DECIMAL_CHUNK_BASE: u64 = 100_000_000;
16const DECIMAL_CHUNK_WIDTH: usize = 8;
17const DECIMAL_BUFFER_LEN: usize = 80;
18const U128_LOW_U32_MASK: u128 = 0xffff_ffff;
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub struct ParseU256Error;
24
25impl fmt::Display for ParseU256Error {
26 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27 formatter.write_str("value is not an unsigned 256-bit integer")
28 }
29}
30
31impl std::error::Error for ParseU256Error {}
32
33#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
40#[repr(transparent)]
41pub struct U256(EthU256);
42
43impl U256 {
44 pub const MIN: Self = Self::ZERO;
46
47 pub const ZERO: Self = Self(EthU256::ZERO);
49
50 pub const ONE: Self = Self(EthU256::ONE);
52
53 pub const MAX: Self = Self(EthU256::MAX);
55
56 #[must_use]
58 pub const fn from_words(high: u128, low: u128) -> Self {
59 Self(EthU256::from_words(high, low))
60 }
61
62 #[must_use]
64 pub const fn into_words(self) -> (u128, u128) {
65 self.0.into_words()
66 }
67
68 #[must_use]
70 pub fn from_be_bytes(bytes: [u8; 32]) -> Self {
71 Self(EthU256::from_be_bytes(bytes))
72 }
73
74 #[must_use]
76 pub fn to_be_bytes(self) -> [u8; 32] {
77 self.0.to_be_bytes()
78 }
79
80 #[must_use]
82 pub const fn to_u128(self) -> Option<u128> {
83 let (high, low) = self.into_words();
84 if high == 0 { Some(low) } else { None }
85 }
86
87 #[must_use]
89 pub fn checked_add(self, rhs: Self) -> Option<Self> {
90 self.0.checked_add(rhs.0).map(Self)
91 }
92
93 #[must_use]
95 pub fn checked_sub(self, rhs: Self) -> Option<Self> {
96 self.0.checked_sub(rhs.0).map(Self)
97 }
98
99 #[must_use]
101 pub fn checked_mul(self, rhs: Self) -> Option<Self> {
102 self.0.checked_mul(rhs.0).map(Self)
103 }
104
105 #[must_use]
107 pub fn checked_div(self, rhs: Self) -> Option<Self> {
108 self.0.checked_div(rhs.0).map(Self)
109 }
110
111 #[must_use]
113 pub fn checked_rem(self, rhs: Self) -> Option<Self> {
114 self.0.checked_rem(rhs.0).map(Self)
115 }
116
117 fn from_little_endian_magnitude(bytes: &[u8]) -> Result<Self, ParseU256Error> {
118 if bytes.len() > 32 {
119 return Err(ParseU256Error);
120 }
121 let mut fixed = [0_u8; 32];
122 for (destination, source) in fixed.iter_mut().rev().zip(bytes) {
123 *destination = *source;
124 }
125 Ok(Self::from_be_bytes(fixed))
126 }
127
128 fn to_candid_nat(self) -> Nat {
129 Nat(BigUint::from_bytes_be(&self.to_be_bytes()))
130 }
131
132 fn decimal_text(self) -> DecimalText {
133 let (high, low) = self.into_words();
134 let mut limbs = [
135 low_u32_from_u128(high >> 96),
136 low_u32_from_u128(high >> 64),
137 low_u32_from_u128(high >> 32),
138 low_u32_from_u128(high),
139 low_u32_from_u128(low >> 96),
140 low_u32_from_u128(low >> 64),
141 low_u32_from_u128(low >> 32),
142 low_u32_from_u128(low),
143 ];
144 let mut bytes = [0_u8; DECIMAL_BUFFER_LEN];
145 let mut start = bytes.len();
146
147 loop {
148 let mut remainder = 0_u64;
149 let mut quotient_is_zero = true;
150 for limb in &mut limbs {
151 let dividend = (remainder << 32) | u64::from(*limb);
152 let quotient = dividend / DECIMAL_CHUNK_BASE;
153 remainder = dividend % DECIMAL_CHUNK_BASE;
154 *limb = u32::try_from(quotient).unwrap_or_default();
156 quotient_is_zero &= quotient == 0;
157 }
158
159 let mut chunk = u32::try_from(remainder).unwrap_or_default();
161 for _ in 0..DECIMAL_CHUNK_WIDTH {
162 start -= 1;
163 bytes[start] = b'0' + u8::try_from(chunk % 10).unwrap_or_default();
164 chunk /= 10;
165 }
166 if quotient_is_zero {
167 break;
168 }
169 }
170 while start + 1 < bytes.len() && bytes[start] == b'0' {
171 start += 1;
172 }
173
174 DecimalText { bytes, start }
175 }
176}
177
178fn low_u32_from_u128(value: u128) -> u32 {
179 u32::try_from(value & U128_LOW_U32_MASK).unwrap_or_default()
180}
181
182struct DecimalText {
183 bytes: [u8; DECIMAL_BUFFER_LEN],
184 start: usize,
185}
186
187impl DecimalText {
188 fn as_str(&self) -> &str {
189 std::str::from_utf8(&self.bytes[self.start..]).unwrap_or_default()
190 }
191}
192
193impl CandidType for U256 {
194 fn ty() -> Type {
195 TypeInner::Nat.into()
196 }
197
198 fn _ty() -> Type {
199 TypeInner::Nat.into()
200 }
201
202 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
203 where
204 S: Serializer,
205 {
206 serializer.serialize_nat(&self.to_candid_nat())
207 }
208}
209
210impl fmt::Debug for U256 {
211 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
212 fmt::Display::fmt(self, formatter)
213 }
214}
215
216impl fmt::Display for U256 {
217 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
218 let text = self.decimal_text();
219 formatter.write_str(text.as_str())
220 }
221}
222
223impl From<u64> for U256 {
224 fn from(value: u64) -> Self {
225 Self(EthU256::from(value))
226 }
227}
228
229impl From<u128> for U256 {
230 fn from(value: u128) -> Self {
231 Self(EthU256::from(value))
232 }
233}
234
235impl FromStr for U256 {
236 type Err = ParseU256Error;
237
238 fn from_str(value: &str) -> Result<Self, Self::Err> {
239 if value.is_empty()
240 || value.len() > MAX_DECIMAL_DIGITS
241 || !value.bytes().all(|byte| byte.is_ascii_digit())
242 {
243 return Err(ParseU256Error);
244 }
245 value
246 .parse::<EthU256>()
247 .map(Self)
248 .map_err(|_| ParseU256Error)
249 }
250}
251
252impl NumericValue for U256 {
253 fn try_to_decimal(&self) -> Option<Decimal> {
254 self.to_u128().and_then(Decimal::from_u128)
255 }
256
257 fn try_from_decimal(value: Decimal) -> Option<Self> {
258 value.to_u128().map(Self::from)
259 }
260}
261
262impl<'de> Deserialize<'de> for U256 {
263 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
264 where
265 D: Deserializer<'de>,
266 {
267 integer_wire::deserialize_integer(deserializer)
268 }
269}
270
271impl IntegerWire for U256 {
272 fn from_signed(value: i64) -> Option<Self> {
273 u64::try_from(value).ok().map(Self::from)
274 }
275
276 fn from_unsigned(value: u64) -> Self {
277 Self::from(value)
278 }
279
280 fn from_wire_bytes(value: &[u8]) -> Option<Self> {
281 Self::from_little_endian_magnitude(integer_wire::unsigned_body(value)?).ok()
282 }
283}
284
285impl Serialize for U256 {
286 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287 where
288 S: SerdeSerializer,
289 {
290 if serializer.is_human_readable() {
291 return serializer.collect_str(self);
292 }
293 if let Some(value) = self.to_u128().and_then(|value| u64::try_from(value).ok()) {
294 return serializer.serialize_u64(value);
295 }
296 let magnitude = self.0.to_le_bytes();
298 let len = magnitude
299 .iter()
300 .rposition(|byte| *byte != 0)
301 .map_or(1, |index| index + 1);
302 let mut bytes = [0_u8; 33];
303 bytes[0] = 1;
304 bytes[1..=len].copy_from_slice(&magnitude[..len]);
305 serializer.serialize_bytes(&bytes[..=len])
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::U256;
312 use crate::{Decimal, NumericValue};
313 use candid::{CandidType, decode_one, encode_one};
314 use num_bigint::BigUint;
315
316 #[test]
317 fn candid_uses_nat_and_rejects_values_above_maximum() {
318 assert_eq!(U256::ty(), candid::Nat::ty());
319
320 let encoded = encode_one(U256::MAX).expect("U256 should encode");
321 assert_eq!(
322 decode_one::<U256>(&encoded).expect("U256 should decode"),
323 U256::MAX
324 );
325
326 let above = candid::Nat(BigUint::from(1_u8) << 256_usize);
327 let encoded = encode_one(above).expect("Nat should encode");
328 assert!(decode_one::<U256>(&encoded).is_err());
329 }
330
331 #[test]
332 fn fixed_bytes_and_decimal_are_exact() {
333 let value = "57896044618658097711785492504343953926634992332820282019728792003956564819968"
334 .parse::<U256>()
335 .expect("2^255 should parse");
336 assert_eq!(value.to_be_bytes()[0], 0x80);
337 assert_eq!(U256::from_be_bytes(value.to_be_bytes()), value);
338 assert_eq!(
339 value.to_string(),
340 "57896044618658097711785492504343953926634992332820282019728792003956564819968"
341 );
342 assert_eq!(U256::ZERO.to_string(), "0");
343 assert_eq!(U256::ONE.to_string(), "1");
344 assert_eq!(U256::MAX.to_string(), u256_max_decimal());
345 }
346
347 #[test]
348 fn checked_arithmetic_enforces_the_u256_domain() {
349 let two = U256::from(2_u64);
350 let three = U256::from(3_u64);
351
352 assert_eq!(two.checked_add(three), Some(U256::from(5_u64)));
353 assert_eq!(three.checked_sub(two), Some(U256::ONE));
354 assert_eq!(two.checked_mul(three), Some(U256::from(6_u64)));
355 assert_eq!(U256::from(7_u64).checked_div(two), Some(three));
356 assert_eq!(U256::from(7_u64).checked_rem(two), Some(U256::ONE));
357 assert_eq!(U256::MAX.checked_add(U256::ONE), None);
358 assert_eq!(U256::ZERO.checked_sub(U256::ONE), None);
359 assert_eq!(U256::MAX.checked_mul(two), None);
360 assert_eq!(U256::ONE.checked_div(U256::ZERO), None);
361 assert_eq!(U256::ONE.checked_rem(U256::ZERO), None);
362 }
363
364 #[test]
365 fn generic_numeric_conversion_is_fallible_without_widening_the_u256_domain() {
366 let value = U256::from(u128::try_from(i128::MAX).expect("i128::MAX should fit u128"));
367 assert_eq!(
368 value.try_to_decimal().and_then(U256::try_from_decimal),
369 Some(value),
370 );
371 assert_eq!(U256::MAX.try_to_decimal(), None);
372 let negative_one = Decimal::from_i128(-1).expect("-1 should be a valid Decimal");
373 assert_eq!(U256::try_from_decimal(negative_one), None);
374 }
375
376 fn u256_max_decimal() -> &'static str {
377 "115792089237316195423570985008687907853269984665640564039457584007913129639935"
378 }
379}