From 014a6fb10042a355985637e8447971ea7e14361d Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 26 Aug 2025 12:37:12 +0200 Subject: [PATCH 1/4] BlockNumber as integer rather than Hex for Sonic RPC Enhance Quantity deserialization to accept numeric values Updated the QuantityVisitor to handle both hex strings and numeric values (u64 and i64) for deserialization. Added tests to verify the correct handling of numeric JSON values. --- hypersync-format/src/types/quantity.rs | 41 ++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/hypersync-format/src/types/quantity.rs b/hypersync-format/src/types/quantity.rs index a86be2f..934966d 100644 --- a/hypersync-format/src/types/quantity.rs +++ b/hypersync-format/src/types/quantity.rs @@ -95,7 +95,7 @@ impl Visitor<'_> for QuantityVisitor { type Value = Quantity; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("hex string for a quantity") + formatter.write_str("hex string or numeric value for a quantity") } fn visit_str(self, value: &str) -> StdResult @@ -106,6 +106,27 @@ impl Visitor<'_> for QuantityVisitor { Ok(Quantity::from(buf)) } + + fn visit_u64(self, value: u64) -> StdResult + where + E: de::Error, + { + // Convert the integer to big-endian bytes and canonicalize + let buf = canonicalize_bytes(value.to_be_bytes().to_vec()); + Ok(Quantity::from(buf)) + } + + fn visit_i64(self, value: i64) -> StdResult + where + E: de::Error, + { + if value < 0 { + return Err(E::custom("negative quantity not allowed")); + } + let unsigned = value as u64; + let buf = canonicalize_bytes(unsigned.to_be_bytes().to_vec()); + Ok(Quantity::from(buf)) + } } impl<'de> Deserialize<'de> for Quantity { @@ -113,7 +134,8 @@ impl<'de> Deserialize<'de> for Quantity { where D: Deserializer<'de>, { - deserializer.deserialize_str(QuantityVisitor) + // Accept either hex strings (0x...) or numeric JSON values + deserializer.deserialize_any(QuantityVisitor) } } @@ -267,4 +289,19 @@ mod tests { assert_de_tokens(&Quantity::from(hex!("00")), &[Token::Str("0x00")]); assert_de_tokens(&Quantity::from(hex!("00")), &[Token::Str("0x0000")]); } + + #[test] + fn test_deserialize_numeric_u64() { + // Numeric JSON values should be accepted (e.g., Sonic timestamps) + assert_de_tokens(&Quantity::from(hex!("66a7c725")), &[Token::U64(0x66a7c725)]); + assert_de_tokens(&Quantity::from(vec![0]), &[Token::U64(0)]); + assert_de_tokens(&Quantity::from(hex!("01")), &[Token::U64(1)]); + } + + #[test] + fn test_deserialize_numeric_i64() { + assert_de_tokens(&Quantity::from(hex!("66a7c725")), &[Token::I64(0x66a7c725)]); + assert_de_tokens(&Quantity::from(vec![0]), &[Token::I64(0)]); + assert_de_tokens(&Quantity::from(hex!("01")), &[Token::I64(1)]); + } } From 99bdd6e5e56fd5c4381e7ceb9480bb60a9d6a3fd Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 26 Aug 2025 12:50:54 +0200 Subject: [PATCH 2/4] bump hypersync format version --- hypersync-format/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypersync-format/Cargo.toml b/hypersync-format/Cargo.toml index b0b21fe..2b3fd17 100644 --- a/hypersync-format/Cargo.toml +++ b/hypersync-format/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hypersync-format" -version = "0.5.3" +version = "0.5.4" edition = "2021" description = "evm format library" license = "MPL-2.0" From 41fd92fbf95f84333aa3f1a09b31645b190a73e6 Mon Sep 17 00:00:00 2001 From: Jono Prest Date: Thu, 4 Sep 2025 11:11:23 +0000 Subject: [PATCH 3/4] Improve deserializer for quantity --- hypersync-format/src/types/quantity.rs | 68 ++++++++++---------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/hypersync-format/src/types/quantity.rs b/hypersync-format/src/types/quantity.rs index 934966d..dc632ae 100644 --- a/hypersync-format/src/types/quantity.rs +++ b/hypersync-format/src/types/quantity.rs @@ -1,6 +1,5 @@ use super::{util::canonicalize_bytes, Hex}; use crate::{Error, Result}; -use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::borrow::Cow; use std::fmt; @@ -89,53 +88,36 @@ impl From<[u8; N]> for Quantity { } } -struct QuantityVisitor; - -impl Visitor<'_> for QuantityVisitor { - type Value = Quantity; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("hex string or numeric value for a quantity") - } - - fn visit_str(self, value: &str) -> StdResult - where - E: de::Error, - { - let buf: Vec = decode_hex(value).map_err(|e| E::custom(e.to_string()))?; - - Ok(Quantity::from(buf)) - } - - fn visit_u64(self, value: u64) -> StdResult - where - E: de::Error, - { - // Convert the integer to big-endian bytes and canonicalize - let buf = canonicalize_bytes(value.to_be_bytes().to_vec()); - Ok(Quantity::from(buf)) - } - - fn visit_i64(self, value: i64) -> StdResult - where - E: de::Error, - { - if value < 0 { - return Err(E::custom("negative quantity not allowed")); - } - let unsigned = value as u64; - let buf = canonicalize_bytes(unsigned.to_be_bytes().to_vec()); - Ok(Quantity::from(buf)) - } -} - impl<'de> Deserialize<'de> for Quantity { fn deserialize(deserializer: D) -> StdResult where D: Deserializer<'de>, { - // Accept either hex strings (0x...) or numeric JSON values - deserializer.deserialize_any(QuantityVisitor) + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrInt { + Str(String), + Int(i64), + } + match StringOrInt::deserialize(deserializer)? { + StringOrInt::Str(value) => match decode_hex(&value) { + Ok(buf) => Ok(Quantity::from(buf)), + Err(e) => Err(serde::de::Error::custom(format!( + "invalid hex string: {}", + e + ))), + }, + StringOrInt::Int(value) => { + if value < 0 { + return Err(serde::de::Error::custom( + "negative int quantity not allowed", + )); + } + // Convert the integer to big-endian bytes and canonicalize + let buf = canonicalize_bytes(value.to_be_bytes().to_vec()); + Ok(Quantity::from(buf)) + } + } } } From 1683e9a4b622d274645f641594927598b93c0561 Mon Sep 17 00:00:00 2001 From: Jono Prest Date: Thu, 4 Sep 2025 11:13:01 +0000 Subject: [PATCH 4/4] Bump versions again for release --- hypersync-client/Cargo.toml | 2 +- hypersync-format/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hypersync-client/Cargo.toml b/hypersync-client/Cargo.toml index 50fc9c7..d14b781 100644 --- a/hypersync-client/Cargo.toml +++ b/hypersync-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hypersync-client" -version = "0.18.4" +version = "0.18.5" edition = "2021" description = "client library for hypersync" license = "MPL-2.0" diff --git a/hypersync-format/Cargo.toml b/hypersync-format/Cargo.toml index 2b3fd17..fe9162a 100644 --- a/hypersync-format/Cargo.toml +++ b/hypersync-format/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hypersync-format" -version = "0.5.4" +version = "0.5.5" edition = "2021" description = "evm format library" license = "MPL-2.0"