From e8e9f6cc048b81ad3d86814b5145acc253d3ce68 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Wed, 1 Nov 2023 13:14:01 +0800 Subject: [PATCH 1/9] feat: accept numbers for `NUM_AS_HEX` (#491) While the official specs indicate that `NUM_AS_HEX` must be strings, some Starknet libraries expect numbers to be acceptable in certain cases. Adding support for this technically breaks the specs but it's acceptable since it's only on the deserialization side. Also changes the `NumAsHex` implementation to remove the unnecessary allocation for `String`. This makes deserialization slightly more efficient. --- starknet-core/src/types/serde_impls.rs | 64 ++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/starknet-core/src/types/serde_impls.rs b/starknet-core/src/types/serde_impls.rs index ac959764..39ee3e49 100644 --- a/starknet-core/src/types/serde_impls.rs +++ b/starknet-core/src/types/serde_impls.rs @@ -1,12 +1,14 @@ -use alloc::{format, string::String}; +use alloc::{fmt::Formatter, format}; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; use serde_with::{DeserializeAs, SerializeAs}; use super::{SyncStatus, SyncStatusType}; pub(crate) struct NumAsHex; +struct NumAsHexVisitor; + impl SerializeAs for NumAsHex { fn serialize_as(value: &u64, serializer: S) -> Result where @@ -21,14 +23,48 @@ impl<'de> DeserializeAs<'de, u64> for NumAsHex { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - match u64::from_str_radix(&value[2..], 16) { + deserializer.deserialize_any(NumAsHexVisitor) + } +} + +impl<'de> Visitor<'de> for NumAsHexVisitor { + type Value = u64; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string or number") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match u64::from_str_radix(v.trim_start_matches("0x"), 16) { Ok(value) => Ok(value), Err(err) => Err(serde::de::Error::custom(format!( "invalid hex string: {err}" ))), } } + + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, + { + match v.try_into() { + Ok(value) => self.visit_u64(value), + Err(_) => Err(serde::de::Error::custom(format!( + "value cannot be negative: {}", + v + ))), + } + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + Ok(v) + } } #[derive(Deserialize)] @@ -231,7 +267,13 @@ mod enum_ser_impls { #[cfg(test)] mod tests { - use super::super::{BlockId, BlockTag, FieldElement}; + use serde::Deserialize; + use serde_with::serde_as; + + use super::{ + super::{BlockId, BlockTag, FieldElement}, + NumAsHex, + }; #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] @@ -251,4 +293,16 @@ mod tests { assert_eq!(serde_json::from_str::(json).unwrap(), block_id); } } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_num_as_hex_deser() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Deserialize)] + struct Value(#[serde_as(as = "NumAsHex")] u64); + + for (num, json) in [(Value(100), "\"0x64\""), (Value(100), "100")].into_iter() { + assert_eq!(serde_json::from_str::(json).unwrap(), num); + } + } } From 27cf6d44ad17893fc0a00d5204167c4db5d84bcd Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Wed, 1 Nov 2023 13:39:21 +0800 Subject: [PATCH 2/9] perf: drop useless allocation in deserialization (#492) --- starknet-core/src/serde/byte_array.rs | 29 ++++++-- starknet-core/src/serde/num_hex.rs | 28 +++++-- .../src/serde/unsigned_field_element.rs | 74 +++++++++++++++---- starknet-core/src/types/eth_address.rs | 25 +++++-- starknet-ff/src/lib.rs | 30 +++++++- 5 files changed, 148 insertions(+), 38 deletions(-) diff --git a/starknet-core/src/serde/byte_array.rs b/starknet-core/src/serde/byte_array.rs index 79d9411e..d5ddd470 100644 --- a/starknet-core/src/serde/byte_array.rs +++ b/starknet-core/src/serde/byte_array.rs @@ -1,8 +1,10 @@ pub mod base64 { - use alloc::{format, string::String, vec::Vec}; + use alloc::{fmt::Formatter, format, vec::Vec}; use base64::{engine::general_purpose::STANDARD, Engine}; - use serde::{Deserialize, Deserializer, Serializer}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct Base64Visitor; pub fn serialize(value: T, serializer: S) -> Result where @@ -16,12 +18,23 @@ pub mod base64 { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - match STANDARD.decode(value) { - Ok(value) => Ok(value), - Err(err) => Err(serde::de::Error::custom(format!( - "invalid base64 string: {err}" - ))), + deserializer.deserialize_any(Base64Visitor) + } + + impl<'de> Visitor<'de> for Base64Visitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + STANDARD + .decode(v) + .map_err(|err| serde::de::Error::custom(format!("invalid base64 string: {err}"))) } } } diff --git a/starknet-core/src/serde/num_hex.rs b/starknet-core/src/serde/num_hex.rs index 9aaf9076..985b9053 100644 --- a/starknet-core/src/serde/num_hex.rs +++ b/starknet-core/src/serde/num_hex.rs @@ -1,7 +1,9 @@ pub mod u64 { - use alloc::{format, string::String}; + use alloc::{fmt::Formatter, format}; - use serde::{Deserialize, Deserializer, Serializer}; + use serde::{de::Visitor, Deserializer, Serializer}; + + struct NumHexVisitor; pub fn serialize(value: &u64, serializer: S) -> Result where @@ -14,12 +16,22 @@ pub mod u64 { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - match u64::from_str_radix(value.trim_start_matches("0x"), 16) { - Ok(value) => Ok(value), - Err(err) => Err(serde::de::Error::custom(format!( - "invalid u64 hex string: {err}" - ))), + deserializer.deserialize_any(NumHexVisitor) + } + + impl<'de> Visitor<'de> for NumHexVisitor { + type Value = u64; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + u64::from_str_radix(v.trim_start_matches("0x"), 16) + .map_err(|err| serde::de::Error::custom(format!("invalid u64 hex string: {err}"))) } } } diff --git a/starknet-core/src/serde/unsigned_field_element.rs b/starknet-core/src/serde/unsigned_field_element.rs index a130366c..1a4d8a53 100644 --- a/starknet-core/src/serde/unsigned_field_element.rs +++ b/starknet-core/src/serde/unsigned_field_element.rs @@ -1,6 +1,9 @@ -use alloc::{format, string::String}; +use alloc::{fmt::Formatter, format}; -use serde::{de::Error as DeError, Deserialize, Deserializer, Serializer}; +use serde::{ + de::{Error as DeError, Visitor}, + Deserializer, Serializer, +}; use serde_with::{DeserializeAs, SerializeAs}; use crate::types::FieldElement; @@ -11,6 +14,10 @@ pub struct UfeHexOption; pub struct UfePendingBlockHash; +struct UfeHexVisitor; +struct UfeHexOptionVisitor; +struct UfePendingBlockHashVisitor; + impl SerializeAs for UfeHex { fn serialize_as(value: &FieldElement, serializer: S) -> Result where @@ -25,11 +32,23 @@ impl<'de> DeserializeAs<'de, FieldElement> for UfeHex { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - match FieldElement::from_hex_be(&value) { - Ok(value) => Ok(value), - Err(err) => Err(DeError::custom(format!("invalid hex string: {err}"))), - } + deserializer.deserialize_any(UfeHexVisitor) + } +} + +impl<'de> Visitor<'de> for UfeHexVisitor { + type Value = FieldElement; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: DeError, + { + FieldElement::from_hex_be(v) + .map_err(|err| DeError::custom(format!("invalid hex string: {err}"))) } } @@ -50,10 +69,24 @@ impl<'de> DeserializeAs<'de, Option> for UfeHexOption { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - match value.as_str() { + deserializer.deserialize_any(UfeHexOptionVisitor) + } +} + +impl<'de> Visitor<'de> for UfeHexOptionVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: DeError, + { + match v { "" => Ok(None), - _ => match FieldElement::from_hex_be(&value) { + _ => match FieldElement::from_hex_be(v) { Ok(value) => Ok(Some(value)), Err(err) => Err(DeError::custom(format!("invalid hex string: {err}"))), }, @@ -79,11 +112,25 @@ impl<'de> DeserializeAs<'de, Option> for UfePendingBlockHash { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - if value.is_empty() || value == "pending" || value == "None" { + deserializer.deserialize_any(UfePendingBlockHashVisitor) + } +} + +impl<'de> Visitor<'de> for UfePendingBlockHashVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: DeError, + { + if v.is_empty() || v == "pending" || v == "None" { Ok(None) } else { - match FieldElement::from_hex_be(&value) { + match FieldElement::from_hex_be(v) { Ok(value) => Ok(Some(value)), Err(err) => Err(DeError::custom(format!("invalid hex string: {err}"))), } @@ -95,6 +142,7 @@ impl<'de> DeserializeAs<'de, Option> for UfePendingBlockHash { mod tests { use super::*; + use serde::Deserialize; use serde_with::serde_as; #[serde_as] diff --git a/starknet-core/src/types/eth_address.rs b/starknet-core/src/types/eth_address.rs index 53d38ad4..21d483cd 100644 --- a/starknet-core/src/types/eth_address.rs +++ b/starknet-core/src/types/eth_address.rs @@ -1,7 +1,7 @@ -use alloc::{format, string::String}; +use alloc::{fmt::Formatter, format}; use core::str::FromStr; -use serde::{Deserialize, Serialize}; +use serde::{de::Visitor, Deserialize, Serialize}; use starknet_ff::FieldElement; // 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF @@ -17,6 +17,8 @@ pub struct EthAddress { inner: [u8; 20], } +struct EthAddressVisitor; + mod errors { use core::fmt::{Display, Formatter, Result}; @@ -84,9 +86,22 @@ impl<'de> Deserialize<'de> for EthAddress { where D: serde::Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - value - .parse() + deserializer.deserialize_any(EthAddressVisitor) + } +} + +impl<'de> Visitor<'de> for EthAddressVisitor { + type Value = EthAddress; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse() .map_err(|err| serde::de::Error::custom(format!("{}", err))) } } diff --git a/starknet-ff/src/lib.rs b/starknet-ff/src/lib.rs index 4a0ff4f4..d3032cf3 100644 --- a/starknet-ff/src/lib.rs +++ b/starknet-ff/src/lib.rs @@ -552,10 +552,18 @@ impl fmt::UpperHex for FieldElement { #[cfg(feature = "serde")] mod serde_field_element { + #[cfg(feature = "std")] + use core::fmt::{Formatter, Result as FmtResult}; + use super::*; #[cfg(not(feature = "std"))] - use alloc::string::{String, ToString}; - use serde::{Deserialize, Serialize}; + use alloc::{ + fmt::{Formatter, Result as FmtResult}, + string::ToString, + }; + use serde::{de::Visitor, Deserialize, Serialize}; + + struct FieldElementVisitor; impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result @@ -571,8 +579,22 @@ mod serde_field_element { where D: serde::Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - Self::from_str(&value).map_err(serde::de::Error::custom) + deserializer.deserialize_any(FieldElementVisitor) + } + } + + impl<'de> Visitor<'de> for FieldElementVisitor { + type Value = FieldElement; + + fn expecting(&self, formatter: &mut Formatter) -> FmtResult { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + FieldElement::from_str(v).map_err(serde::de::Error::custom) } } } From 14974468e161365c27a86704b4a0034b2229fc2f Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Wed, 1 Nov 2023 13:41:03 +0800 Subject: [PATCH 3/9] release: bump starknet-ff and starknet-core (#493) --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- examples/starknet-wasm/Cargo.toml | 2 +- starknet-accounts/Cargo.toml | 2 +- starknet-contract/Cargo.toml | 2 +- starknet-core/Cargo.toml | 4 ++-- starknet-crypto-codegen/Cargo.toml | 2 +- starknet-crypto/Cargo.toml | 2 +- starknet-curve/Cargo.toml | 2 +- starknet-ff/Cargo.toml | 2 +- starknet-macros/Cargo.toml | 2 +- starknet-providers/Cargo.toml | 2 +- starknet-signers/Cargo.toml | 2 +- 13 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9d3ce34..895d3e16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1687,7 +1687,7 @@ dependencies = [ [[package]] name = "starknet-core" -version = "0.7.0" +version = "0.7.1" dependencies = [ "base64 0.21.0", "criterion", @@ -1755,7 +1755,7 @@ dependencies = [ [[package]] name = "starknet-ff" -version = "0.3.4" +version = "0.3.5" dependencies = [ "ark-ff", "bigdecimal", diff --git a/Cargo.toml b/Cargo.toml index e898cfac..62c2934c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,9 @@ members = [ all-features = true [dependencies] -starknet-ff = { version = "0.3.4", path = "./starknet-ff", default-features = false } +starknet-ff = { version = "0.3.5", path = "./starknet-ff", default-features = false } starknet-crypto = { version = "0.6.1", path = "./starknet-crypto" } -starknet-core = { version = "0.7.0", path = "./starknet-core", default-features = false } +starknet-core = { version = "0.7.1", path = "./starknet-core", default-features = false } starknet-providers = { version = "0.7.0", path = "./starknet-providers" } starknet-contract = { version = "0.6.0", path = "./starknet-contract" } starknet-signers = { version = "0.5.0", path = "./starknet-signers" } diff --git a/examples/starknet-wasm/Cargo.toml b/examples/starknet-wasm/Cargo.toml index 48c05114..4b8300f1 100644 --- a/examples/starknet-wasm/Cargo.toml +++ b/examples/starknet-wasm/Cargo.toml @@ -19,7 +19,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -starknet-ff = { version = "0.3.4", path = "../../starknet-ff" } +starknet-ff = { version = "0.3.5", path = "../../starknet-ff" } starknet-crypto = { version = "0.6.1", path = "../../starknet-crypto" } console_error_panic_hook = { version = "0.1.7", optional = true } wasm-bindgen = "0.2.84" diff --git a/starknet-accounts/Cargo.toml b/starknet-accounts/Cargo.toml index 95cd090e..539f1fa8 100644 --- a/starknet-accounts/Cargo.toml +++ b/starknet-accounts/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["ethereum", "starknet", "web3"] exclude = ["test-data/**"] [dependencies] -starknet-core = { version = "0.7.0", path = "../starknet-core" } +starknet-core = { version = "0.7.1", path = "../starknet-core" } starknet-providers = { version = "0.7.0", path = "../starknet-providers" } starknet-signers = { version = "0.5.0", path = "../starknet-signers" } async-trait = "0.1.68" diff --git a/starknet-contract/Cargo.toml b/starknet-contract/Cargo.toml index 835d55be..4b1d5adb 100644 --- a/starknet-contract/Cargo.toml +++ b/starknet-contract/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["ethereum", "starknet", "web3"] exclude = ["test-data/**"] [dependencies] -starknet-core = { version = "0.7.0", path = "../starknet-core" } +starknet-core = { version = "0.7.1", path = "../starknet-core" } starknet-providers = { version = "0.7.0", path = "../starknet-providers" } starknet-accounts = { version = "0.6.0", path = "../starknet-accounts" } serde = { version = "1.0.160", features = ["derive"] } diff --git a/starknet-core/Cargo.toml b/starknet-core/Cargo.toml index 8a8eeb1b..fa0a9073 100644 --- a/starknet-core/Cargo.toml +++ b/starknet-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starknet-core" -version = "0.7.0" +version = "0.7.1" authors = ["Jonathan LEI "] license = "MIT OR Apache-2.0" edition = "2021" @@ -18,7 +18,7 @@ all-features = true [dependencies] starknet-crypto = { version = "0.6.1", path = "../starknet-crypto", default-features = false, features = ["alloc"] } -starknet-ff = { version = "0.3.4", path = "../starknet-ff", default-features = false, features = ["serde"] } +starknet-ff = { version = "0.3.5", path = "../starknet-ff", default-features = false, features = ["serde"] } base64 = { version = "0.21.0", default-features = false, features = ["alloc"] } flate2 = { version = "1.0.25", optional = true } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } diff --git a/starknet-crypto-codegen/Cargo.toml b/starknet-crypto-codegen/Cargo.toml index 87034a67..b4fa8c15 100644 --- a/starknet-crypto-codegen/Cargo.toml +++ b/starknet-crypto-codegen/Cargo.toml @@ -17,5 +17,5 @@ proc-macro = true [dependencies] starknet-curve = { version = "0.4.0", path = "../starknet-curve" } -starknet-ff = { version = "0.3.4", path = "../starknet-ff", default-features = false } +starknet-ff = { version = "0.3.5", path = "../starknet-ff", default-features = false } syn = { version = "2.0.15", default-features = false } diff --git a/starknet-crypto/Cargo.toml b/starknet-crypto/Cargo.toml index 32ef67d4..e0f18e18 100644 --- a/starknet-crypto/Cargo.toml +++ b/starknet-crypto/Cargo.toml @@ -16,7 +16,7 @@ exclude = ["test-data/**"] [dependencies] starknet-crypto-codegen = { version = "0.3.2", path = "../starknet-crypto-codegen" } starknet-curve = { version = "0.4.0", path = "../starknet-curve" } -starknet-ff = { version = "0.3.4", path = "../starknet-ff", default-features = false } +starknet-ff = { version = "0.3.5", path = "../starknet-ff", default-features = false } crypto-bigint = { version = "0.5.1", default-features = false, features = ["generic-array", "zeroize"] } hmac = { version = "0.12.1", default-features = false } num-bigint = { version = "0.4.3", default-features = false } diff --git a/starknet-curve/Cargo.toml b/starknet-curve/Cargo.toml index 9a9f3976..3dd29224 100644 --- a/starknet-curve/Cargo.toml +++ b/starknet-curve/Cargo.toml @@ -13,4 +13,4 @@ Stark curve keywords = ["ethereum", "starknet", "web3", "no_std"] [dependencies] -starknet-ff = { version = "0.3.4", path = "../starknet-ff", default-features = false } +starknet-ff = { version = "0.3.5", path = "../starknet-ff", default-features = false } diff --git a/starknet-ff/Cargo.toml b/starknet-ff/Cargo.toml index 81434578..87c6217b 100644 --- a/starknet-ff/Cargo.toml +++ b/starknet-ff/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starknet-ff" -version = "0.3.4" +version = "0.3.5" authors = ["Jonathan LEI "] license = "MIT OR Apache-2.0" edition = "2021" diff --git a/starknet-macros/Cargo.toml b/starknet-macros/Cargo.toml index eee8f90f..97ff9a46 100644 --- a/starknet-macros/Cargo.toml +++ b/starknet-macros/Cargo.toml @@ -16,7 +16,7 @@ keywords = ["ethereum", "starknet", "web3"] proc-macro = true [dependencies] -starknet-core = { version = "0.7.0", path = "../starknet-core" } +starknet-core = { version = "0.7.1", path = "../starknet-core" } syn = "2.0.15" [features] diff --git a/starknet-providers/Cargo.toml b/starknet-providers/Cargo.toml index 49f40fe8..aab9dd6a 100644 --- a/starknet-providers/Cargo.toml +++ b/starknet-providers/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["ethereum", "starknet", "web3"] exclude = ["test-data/**"] [dependencies] -starknet-core = { version = "0.7.0", path = "../starknet-core" } +starknet-core = { version = "0.7.1", path = "../starknet-core" } async-trait = "0.1.68" auto_impl = "1.0.1" ethereum-types = "0.14.1" diff --git a/starknet-signers/Cargo.toml b/starknet-signers/Cargo.toml index 362dd706..16f20263 100644 --- a/starknet-signers/Cargo.toml +++ b/starknet-signers/Cargo.toml @@ -13,7 +13,7 @@ Starknet signer implementations keywords = ["ethereum", "starknet", "web3"] [dependencies] -starknet-core = { version = "0.7.0", path = "../starknet-core" } +starknet-core = { version = "0.7.1", path = "../starknet-core" } starknet-crypto = { version = "0.6.1", path = "../starknet-crypto" } async-trait = "0.1.68" auto_impl = "1.0.1" From 925eb66c1c209c4d35cde5bd46a741a6c24c5b12 Mon Sep 17 00:00:00 2001 From: Francesco Ceccon Date: Fri, 3 Nov 2023 06:21:06 +0100 Subject: [PATCH 4/9] fix: execution status revert reason field is optional (#494) The Starknet JSON-RPC spec defines this field as optional. This commit fixes compatibility with nodes like Juno that don't include this field if the revert reason is empty. --- starknet-core/src/types/execution_result.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/starknet-core/src/types/execution_result.rs b/starknet-core/src/types/execution_result.rs index 3479d554..43e8f652 100644 --- a/starknet-core/src/types/execution_result.rs +++ b/starknet-core/src/types/execution_result.rs @@ -68,13 +68,12 @@ impl<'de> Deserialize<'de> for ExecutionResult { match (raw.execution_status, raw.revert_reason) { (TransactionExecutionStatus::Succeeded, None) => Ok(Self::Succeeded), - (TransactionExecutionStatus::Reverted, Some(reason)) => Ok(Self::Reverted { reason }), + (TransactionExecutionStatus::Reverted, reason) => Ok(Self::Reverted { + reason: reason.unwrap_or_default(), + }), (TransactionExecutionStatus::Succeeded, Some(_)) => Err(serde::de::Error::custom( "field `revert_reason` must not exist when `execution_status` is `SUCCEEDED`", )), - (TransactionExecutionStatus::Reverted, None) => Err(serde::de::Error::custom( - "field `revert_reason` missing when `execution_status` is `REVERTED`", - )), } } } From e343224266cf5e8a73a70e32670e539b57921070 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Fri, 3 Nov 2023 23:33:13 +0800 Subject: [PATCH 5/9] feat: new type `MsgToL2` with parsing and hashing (#495) --- starknet-core/src/types/mod.rs | 99 ++++++++++++++++++++++++++++++++++ starknet-core/src/types/msg.rs | 93 ++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 starknet-core/src/types/msg.rs diff --git a/starknet-core/src/types/mod.rs b/starknet-core/src/types/mod.rs index dcb698db..609a8288 100644 --- a/starknet-core/src/types/mod.rs +++ b/starknet-core/src/types/mod.rs @@ -42,6 +42,9 @@ pub use eth_address::EthAddress; mod execution_result; pub use execution_result::ExecutionResult; +mod msg; +pub use msg::MsgToL2; + // TODO: move generated request code to `starknet-providers` pub mod requests; @@ -262,6 +265,36 @@ pub enum ExecuteInvocation { Reverted(RevertedInvocation), } +mod errors { + use core::fmt::{Display, Formatter, Result}; + + #[derive(Debug)] + pub enum ParseMsgToL2Error { + EmptyCalldata, + FromAddressOutOfRange, + } + + #[cfg(feature = "std")] + impl std::error::Error for ParseMsgToL2Error {} + + impl Display for ParseMsgToL2Error { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::EmptyCalldata => { + write!( + f, + "calldata must contain at least 1 element for from_address" + ) + } + Self::FromAddressOutOfRange => { + write!(f, "from_address is larger than 20 bytes") + } + } + } + } +} +pub use errors::ParseMsgToL2Error; + impl MaybePendingBlockWithTxHashes { pub fn transactions(&self) -> &[FieldElement] { match self { @@ -392,6 +425,24 @@ impl PendingTransactionReceipt { } } +impl L1HandlerTransaction { + pub fn parse_msg_to_l2(&self) -> Result { + if self.calldata.is_empty() { + return Err(ParseMsgToL2Error::EmptyCalldata); + } + + Ok(MsgToL2 { + from_address: self.calldata[0] + .try_into() + .map_err(|_| ParseMsgToL2Error::FromAddressOutOfRange)?, + to_address: self.contract_address, + selector: self.entry_point_selector, + payload: self.calldata[1..].to_vec(), + nonce: self.nonce, + }) + } +} + impl AsRef for BlockId { fn as_ref(&self) -> &BlockId { self @@ -434,6 +485,14 @@ impl AsRef for BroadcastedDeployAccountTran } } +impl TryFrom<&L1HandlerTransaction> for MsgToL2 { + type Error = ParseMsgToL2Error; + + fn try_from(value: &L1HandlerTransaction) -> Result { + value.parse_msg_to_l2() + } +} + impl TryFrom for StarknetError { type Error = (); @@ -492,4 +551,44 @@ mod tests { let parsed_object = serde_json::from_str::(raw).unwrap(); assert!(parsed_object.is_query); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_parse_msg_to_l2() { + let l1_handler_tx = L1HandlerTransaction { + transaction_hash: FieldElement::from_hex_be( + "0x374286ae28f201e61ffbc5b022cc9701208640b405ea34ea9799f97d5d2d23c", + ) + .unwrap(), + version: 0, + nonce: 775628, + contract_address: FieldElement::from_hex_be( + "0x73314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82", + ) + .unwrap(), + entry_point_selector: FieldElement::from_hex_be( + "0x2d757788a8d8d6f21d1cd40bce38a8222d70654214e96ff95d8086e684fbee5", + ) + .unwrap(), + calldata: vec![ + FieldElement::from_hex_be("0xc3511006c04ef1d78af4c8e0e74ec18a6e64ff9e").unwrap(), + FieldElement::from_hex_be( + "0x689ead7d814e51ed93644bc145f0754839b8dcb340027ce0c30953f38f55d7", + ) + .unwrap(), + FieldElement::from_hex_be("0x2c68af0bb140000").unwrap(), + FieldElement::from_hex_be("0x0").unwrap(), + ], + }; + + let msg_to_l2 = l1_handler_tx.parse_msg_to_l2().unwrap(); + + let expected_hash: [u8; 32] = + hex::decode("c51a543ef9563ad2545342b390b67edfcddf9886aa36846cf70382362fc5fab3") + .unwrap() + .try_into() + .unwrap(); + + assert_eq!(msg_to_l2.hash(), expected_hash); + } } diff --git a/starknet-core/src/types/msg.rs b/starknet-core/src/types/msg.rs new file mode 100644 index 00000000..da381986 --- /dev/null +++ b/starknet-core/src/types/msg.rs @@ -0,0 +1,93 @@ +use alloc::vec::Vec; + +use sha3::{Digest, Keccak256}; +use starknet_ff::FieldElement; + +use super::EthAddress; + +#[derive(Debug, Clone)] +pub struct MsgToL2 { + pub from_address: EthAddress, + pub to_address: FieldElement, + pub selector: FieldElement, + pub payload: Vec, + pub nonce: u64, +} + +impl MsgToL2 { + /// Calculates the message hash based on the algorithm documented here: + /// + /// https://docs.starknet.io/documentation/architecture_and_concepts/L1-L2_Communication/messaging-mechanism/ + pub fn hash(&self) -> [u8; 32] { + let mut hasher = Keccak256::new(); + + // FromAddress + hasher.update([0u8; 12]); + hasher.update(self.from_address.as_bytes()); + + // ToAddress + hasher.update(self.to_address.to_bytes_be()); + + // Nonce + hasher.update([0u8; 24]); + hasher.update(self.nonce.to_be_bytes()); + + // Selector + hasher.update(self.selector.to_bytes_be()); + + // Payload.length + hasher.update([0u8; 24]); + hasher.update((self.payload.len() as u64).to_be_bytes()); + + // Payload + for item in self.payload.iter() { + hasher.update(item.to_bytes_be()); + } + + let hash = hasher.finalize(); + + // Because we know hash is always 32 bytes + unsafe { *(hash[..].as_ptr() as *const [u8; 32]) } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_msg_to_l2_hash() { + // Goerli-1 tx: 0374286ae28f201e61ffbc5b022cc9701208640b405ea34ea9799f97d5d2d23c + + let msg = MsgToL2 { + from_address: EthAddress::from_hex("0xc3511006C04EF1d78af4C8E0e74Ec18A6E64Ff9e") + .unwrap(), + to_address: FieldElement::from_hex_be( + "0x73314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82", + ) + .unwrap(), + selector: FieldElement::from_hex_be( + "0x2d757788a8d8d6f21d1cd40bce38a8222d70654214e96ff95d8086e684fbee5", + ) + .unwrap(), + payload: vec![ + FieldElement::from_hex_be( + "0x689ead7d814e51ed93644bc145f0754839b8dcb340027ce0c30953f38f55d7", + ) + .unwrap(), + FieldElement::from_hex_be("0x2c68af0bb140000").unwrap(), + FieldElement::from_hex_be("0x0").unwrap(), + ], + nonce: 775628, + }; + + let expected_hash: [u8; 32] = + hex::decode("c51a543ef9563ad2545342b390b67edfcddf9886aa36846cf70382362fc5fab3") + .unwrap() + .try_into() + .unwrap(); + + assert_eq!(msg.hash(), expected_hash); + } +} From a15e1f9f37a4e90500c626144fe8ee4920462e82 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Sat, 4 Nov 2023 00:20:28 +0800 Subject: [PATCH 6/9] feat: add `Hash256` type (#496) --- starknet-core/src/types/hash_256.rs | 177 ++++++++++++++++++++++++++++ starknet-core/src/types/mod.rs | 9 +- starknet-core/src/types/msg.rs | 12 +- 3 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 starknet-core/src/types/hash_256.rs diff --git a/starknet-core/src/types/hash_256.rs b/starknet-core/src/types/hash_256.rs new file mode 100644 index 00000000..a75c9896 --- /dev/null +++ b/starknet-core/src/types/hash_256.rs @@ -0,0 +1,177 @@ +use alloc::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + format, + str::FromStr, +}; + +use serde::{de::Visitor, Deserialize, Serialize}; +use starknet_ff::FieldElement; + +const HASH_256_BYTE_COUNT: usize = 32; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Hash256 { + inner: [u8; HASH_256_BYTE_COUNT], +} + +struct Hash256Visitor; + +mod errors { + use core::fmt::{Display, Formatter, Result}; + + #[derive(Debug)] + pub enum FromHexError { + UnexpectedLength, + InvalidHexString, + } + + #[derive(Debug)] + pub struct ToFieldElementError; + + #[cfg(feature = "std")] + impl std::error::Error for FromHexError {} + + #[cfg(feature = "std")] + impl std::error::Error for ToFieldElementError {} + + impl Display for FromHexError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::UnexpectedLength => { + write!(f, "unexpected length for 256-bit hash") + } + Self::InvalidHexString => { + write!(f, "invalid hex string") + } + } + } + } + + impl Display for ToFieldElementError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "hash value out of range for FieldElement") + } + } +} +pub use errors::{FromHexError, ToFieldElementError}; + +impl Hash256 { + pub fn from_bytes(bytes: [u8; HASH_256_BYTE_COUNT]) -> Self { + Self { inner: bytes } + } + + pub fn from_hex(hex: &str) -> Result { + hex.parse() + } + + pub fn from_felt(felt: &FieldElement) -> Self { + felt.into() + } + + pub fn as_bytes(&self) -> &[u8; HASH_256_BYTE_COUNT] { + &self.inner + } +} + +impl Serialize for Hash256 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!("0x{}", hex::encode(self.inner))) + } +} + +impl<'de> Deserialize<'de> for Hash256 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(Hash256Visitor) + } +} + +impl<'de> Visitor<'de> for Hash256Visitor { + type Value = Hash256; + + fn expecting(&self, formatter: &mut Formatter) -> alloc::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse() + .map_err(|err| serde::de::Error::custom(format!("{}", err))) + } +} + +impl FromStr for Hash256 { + type Err = FromHexError; + + fn from_str(s: &str) -> Result { + let value = s.trim_start_matches("0x"); + + let hex_chars_len = value.len(); + let expected_hex_length = HASH_256_BYTE_COUNT * 2; + + let parsed_bytes: [u8; HASH_256_BYTE_COUNT] = if hex_chars_len == expected_hex_length { + let mut buffer = [0u8; HASH_256_BYTE_COUNT]; + hex::decode_to_slice(value, &mut buffer).map_err(|_| FromHexError::InvalidHexString)?; + buffer + } else if hex_chars_len < expected_hex_length { + let mut padded_hex = str::repeat("0", expected_hex_length - hex_chars_len); + padded_hex.push_str(value); + + let mut buffer = [0u8; HASH_256_BYTE_COUNT]; + hex::decode_to_slice(&padded_hex, &mut buffer) + .map_err(|_| FromHexError::InvalidHexString)?; + buffer + } else { + return Err(FromHexError::UnexpectedLength); + }; + + Ok(Self::from_bytes(parsed_bytes)) + } +} + +impl Debug for Hash256 { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "0x{}", hex::encode(self.inner)) + } +} + +impl Display for Hash256 { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "0x{}", hex::encode(self.inner)) + } +} + +impl From for Hash256 { + fn from(value: FieldElement) -> Self { + (&value).into() + } +} + +impl From<&FieldElement> for Hash256 { + fn from(value: &FieldElement) -> Self { + Self::from_bytes(value.to_bytes_be()) + } +} + +impl TryFrom for FieldElement { + type Error = ToFieldElementError; + + fn try_from(value: Hash256) -> Result { + (&value).try_into() + } +} + +impl TryFrom<&Hash256> for FieldElement { + type Error = ToFieldElementError; + + fn try_from(value: &Hash256) -> Result { + FieldElement::from_bytes_be(&value.inner).map_err(|_| ToFieldElementError) + } +} diff --git a/starknet-core/src/types/mod.rs b/starknet-core/src/types/mod.rs index 609a8288..18ff37aa 100644 --- a/starknet-core/src/types/mod.rs +++ b/starknet-core/src/types/mod.rs @@ -39,6 +39,9 @@ pub use codegen::{ pub mod eth_address; pub use eth_address::EthAddress; +pub mod hash_256; +pub use hash_256::Hash256; + mod execution_result; pub use execution_result::ExecutionResult; @@ -583,10 +586,8 @@ mod tests { let msg_to_l2 = l1_handler_tx.parse_msg_to_l2().unwrap(); - let expected_hash: [u8; 32] = - hex::decode("c51a543ef9563ad2545342b390b67edfcddf9886aa36846cf70382362fc5fab3") - .unwrap() - .try_into() + let expected_hash = + Hash256::from_hex("c51a543ef9563ad2545342b390b67edfcddf9886aa36846cf70382362fc5fab3") .unwrap(); assert_eq!(msg_to_l2.hash(), expected_hash); diff --git a/starknet-core/src/types/msg.rs b/starknet-core/src/types/msg.rs index da381986..da5ad5e4 100644 --- a/starknet-core/src/types/msg.rs +++ b/starknet-core/src/types/msg.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use sha3::{Digest, Keccak256}; use starknet_ff::FieldElement; -use super::EthAddress; +use super::{EthAddress, Hash256}; #[derive(Debug, Clone)] pub struct MsgToL2 { @@ -18,7 +18,7 @@ impl MsgToL2 { /// Calculates the message hash based on the algorithm documented here: /// /// https://docs.starknet.io/documentation/architecture_and_concepts/L1-L2_Communication/messaging-mechanism/ - pub fn hash(&self) -> [u8; 32] { + pub fn hash(&self) -> Hash256 { let mut hasher = Keccak256::new(); // FromAddress @@ -47,7 +47,7 @@ impl MsgToL2 { let hash = hasher.finalize(); // Because we know hash is always 32 bytes - unsafe { *(hash[..].as_ptr() as *const [u8; 32]) } + Hash256::from_bytes(unsafe { *(hash[..].as_ptr() as *const [u8; 32]) }) } } @@ -82,10 +82,8 @@ mod tests { nonce: 775628, }; - let expected_hash: [u8; 32] = - hex::decode("c51a543ef9563ad2545342b390b67edfcddf9886aa36846cf70382362fc5fab3") - .unwrap() - .try_into() + let expected_hash = + Hash256::from_hex("c51a543ef9563ad2545342b390b67edfcddf9886aa36846cf70382362fc5fab3") .unwrap(); assert_eq!(msg.hash(), expected_hash); From 97c421eeb94d93562e3631c83b877b7ec86353f9 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Sat, 4 Nov 2023 01:25:53 +0800 Subject: [PATCH 7/9] test: re-enable ignored tests (#497) --- starknet-providers/tests/jsonrpc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/starknet-providers/tests/jsonrpc.rs b/starknet-providers/tests/jsonrpc.rs index 3dce9fc9..d2063187 100644 --- a/starknet-providers/tests/jsonrpc.rs +++ b/starknet-providers/tests/jsonrpc.rs @@ -788,7 +788,6 @@ async fn jsonrpc_trace_invoke() { } #[tokio::test] -#[ignore = "disabled until pathfinder PR #1457 is released"] async fn jsonrpc_trace_invoke_reverted() { let rpc_client = create_jsonrpc_client(); From 94c3b6fdb5d25674a1b17b344d4af3e99d440dcc Mon Sep 17 00:00:00 2001 From: glihm Date: Fri, 3 Nov 2023 20:48:53 -0600 Subject: [PATCH 8/9] feat: `MsgToL1` hash computation (#476) --- starknet-core/src/types/msg.rs | 63 +++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/starknet-core/src/types/msg.rs b/starknet-core/src/types/msg.rs index da5ad5e4..aa621fbe 100644 --- a/starknet-core/src/types/msg.rs +++ b/starknet-core/src/types/msg.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use sha3::{Digest, Keccak256}; use starknet_ff::FieldElement; -use super::{EthAddress, Hash256}; +use super::{EthAddress, Hash256, MsgToL1}; #[derive(Debug, Clone)] pub struct MsgToL2 { @@ -51,6 +51,35 @@ impl MsgToL2 { } } +impl MsgToL1 { + /// Calculates the message hash based on the algorithm documented here: + /// + /// https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/messaging-mechanism/#structure_and_hashing_l2-l1 + pub fn hash(&self) -> Hash256 { + let mut hasher = Keccak256::new(); + + // FromAddress + hasher.update(self.from_address.to_bytes_be()); + + // ToAddress + hasher.update(self.to_address.to_bytes_be()); + + // Payload.length + hasher.update([0u8; 24]); + hasher.update((self.payload.len() as u64).to_be_bytes()); + + // Payload + for item in self.payload.iter() { + hasher.update(item.to_bytes_be()); + } + + let hash = hasher.finalize(); + + // Because we know hash is always 32 bytes + Hash256::from_bytes(unsafe { *(hash[..].as_ptr() as *const [u8; 32]) }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -88,4 +117,36 @@ mod tests { assert_eq!(msg.hash(), expected_hash); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_msg_to_l1_hash() { + // Goerli-1 tx (L1): 59e37da138dcf7ab9ca4fc7fde15d3f113a06781c4181dfcf0d74753023060b1 Log #40. + // Goerli-1 tx (L2): 4e0bbc07ff29e5df13dfbcb7e4746fdde52c3649a6a69bd86b15397769722fd + + let msg = MsgToL1 { + from_address: FieldElement::from_hex_be( + "0x0164cba33fb7152531f6b4cfc3fff26b4d7b26b4900e0881042edd607b428a92", + ) + .unwrap(), + to_address: FieldElement::from_hex_be( + "0x000000000000000000000000b6dbfaa86bb683152e4fc2401260f9ca249519c0", + ) + .unwrap(), + payload: vec![ + FieldElement::from_hex_be("0x0").unwrap(), + FieldElement::from_hex_be("0x0").unwrap(), + FieldElement::from_hex_be("0x0182b8").unwrap(), + FieldElement::from_hex_be("0x0").unwrap(), + FieldElement::from_hex_be("0x0384").unwrap(), + FieldElement::from_hex_be("0x0").unwrap(), + ], + }; + + let expected_hash = + Hash256::from_hex("0x326a04493fc8f24ac6c6ae7bdba23243ce03ec3aae53f0ed3a0d686eb8cac930") + .unwrap(); + + assert_eq!(msg.hash(), expected_hash); + } } From 5f1e5cd6f6552eac450811c01c861a405e068637 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Sat, 4 Nov 2023 13:44:00 +0800 Subject: [PATCH 9/9] release: bump starknet-core to 0.7.2 (#498) --- Cargo.lock | 2 +- Cargo.toml | 2 +- starknet-accounts/Cargo.toml | 2 +- starknet-contract/Cargo.toml | 2 +- starknet-core/Cargo.toml | 2 +- starknet-macros/Cargo.toml | 2 +- starknet-providers/Cargo.toml | 2 +- starknet-signers/Cargo.toml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 895d3e16..d7cefa3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1687,7 +1687,7 @@ dependencies = [ [[package]] name = "starknet-core" -version = "0.7.1" +version = "0.7.2" dependencies = [ "base64 0.21.0", "criterion", diff --git a/Cargo.toml b/Cargo.toml index 62c2934c..16c657fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ all-features = true [dependencies] starknet-ff = { version = "0.3.5", path = "./starknet-ff", default-features = false } starknet-crypto = { version = "0.6.1", path = "./starknet-crypto" } -starknet-core = { version = "0.7.1", path = "./starknet-core", default-features = false } +starknet-core = { version = "0.7.2", path = "./starknet-core", default-features = false } starknet-providers = { version = "0.7.0", path = "./starknet-providers" } starknet-contract = { version = "0.6.0", path = "./starknet-contract" } starknet-signers = { version = "0.5.0", path = "./starknet-signers" } diff --git a/starknet-accounts/Cargo.toml b/starknet-accounts/Cargo.toml index 539f1fa8..f9805f56 100644 --- a/starknet-accounts/Cargo.toml +++ b/starknet-accounts/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["ethereum", "starknet", "web3"] exclude = ["test-data/**"] [dependencies] -starknet-core = { version = "0.7.1", path = "../starknet-core" } +starknet-core = { version = "0.7.2", path = "../starknet-core" } starknet-providers = { version = "0.7.0", path = "../starknet-providers" } starknet-signers = { version = "0.5.0", path = "../starknet-signers" } async-trait = "0.1.68" diff --git a/starknet-contract/Cargo.toml b/starknet-contract/Cargo.toml index 4b1d5adb..8958d0c9 100644 --- a/starknet-contract/Cargo.toml +++ b/starknet-contract/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["ethereum", "starknet", "web3"] exclude = ["test-data/**"] [dependencies] -starknet-core = { version = "0.7.1", path = "../starknet-core" } +starknet-core = { version = "0.7.2", path = "../starknet-core" } starknet-providers = { version = "0.7.0", path = "../starknet-providers" } starknet-accounts = { version = "0.6.0", path = "../starknet-accounts" } serde = { version = "1.0.160", features = ["derive"] } diff --git a/starknet-core/Cargo.toml b/starknet-core/Cargo.toml index fa0a9073..64c4d5b9 100644 --- a/starknet-core/Cargo.toml +++ b/starknet-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starknet-core" -version = "0.7.1" +version = "0.7.2" authors = ["Jonathan LEI "] license = "MIT OR Apache-2.0" edition = "2021" diff --git a/starknet-macros/Cargo.toml b/starknet-macros/Cargo.toml index 97ff9a46..27634105 100644 --- a/starknet-macros/Cargo.toml +++ b/starknet-macros/Cargo.toml @@ -16,7 +16,7 @@ keywords = ["ethereum", "starknet", "web3"] proc-macro = true [dependencies] -starknet-core = { version = "0.7.1", path = "../starknet-core" } +starknet-core = { version = "0.7.2", path = "../starknet-core" } syn = "2.0.15" [features] diff --git a/starknet-providers/Cargo.toml b/starknet-providers/Cargo.toml index aab9dd6a..7b1587fc 100644 --- a/starknet-providers/Cargo.toml +++ b/starknet-providers/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["ethereum", "starknet", "web3"] exclude = ["test-data/**"] [dependencies] -starknet-core = { version = "0.7.1", path = "../starknet-core" } +starknet-core = { version = "0.7.2", path = "../starknet-core" } async-trait = "0.1.68" auto_impl = "1.0.1" ethereum-types = "0.14.1" diff --git a/starknet-signers/Cargo.toml b/starknet-signers/Cargo.toml index 16f20263..3de88b58 100644 --- a/starknet-signers/Cargo.toml +++ b/starknet-signers/Cargo.toml @@ -13,7 +13,7 @@ Starknet signer implementations keywords = ["ethereum", "starknet", "web3"] [dependencies] -starknet-core = { version = "0.7.1", path = "../starknet-core" } +starknet-core = { version = "0.7.2", path = "../starknet-core" } starknet-crypto = { version = "0.6.1", path = "../starknet-crypto" } async-trait = "0.1.68" auto_impl = "1.0.1"