From 865007c399cb9b9ba3c4a499be652c7fe1000a4b Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 23 Sep 2023 21:06:29 -0600 Subject: [PATCH 01/31] add cairo_types and abi parser --- .../src/abi/cairo_types/error.rs | 31 ++ starknet-contract/src/abi/cairo_types/mod.rs | 52 +++ .../src/abi/cairo_types/types/array.rs | 131 +++++++ .../src/abi/cairo_types/types/boolean.rs | 45 +++ .../src/abi/cairo_types/types/felt.rs | 56 +++ .../src/abi/cairo_types/types/integers.rs | 227 +++++++++++ .../src/abi/cairo_types/types/mod.rs | 8 + .../src/abi/cairo_types/types/option.rs | 127 +++++++ .../src/abi/cairo_types/types/result.rs | 98 +++++ .../src/abi/cairo_types/types/starknet.rs | 139 +++++++ .../src/abi/cairo_types/types/tuple.rs | 93 +++++ starknet-contract/src/abi/mod.rs | 2 + .../src/abi/parser/abi_types/array.rs | 189 ++++++++++ .../src/abi/parser/abi_types/basic.rs | 156 ++++++++ .../src/abi/parser/abi_types/generic.rs | 355 ++++++++++++++++++ .../src/abi/parser/abi_types/mod.rs | 302 +++++++++++++++ .../src/abi/parser/abi_types/tuple.rs | 262 +++++++++++++ .../src/abi/parser/cairo_enum.rs | 81 ++++ .../src/abi/parser/cairo_event.rs | 108 ++++++ .../src/abi/parser/cairo_function.rs | 46 +++ .../src/abi/parser/cairo_struct.rs | 80 ++++ starknet-contract/src/abi/parser/mod.rs | 27 ++ starknet-contract/src/lib.rs | 3 + 23 files changed, 2618 insertions(+) create mode 100644 starknet-contract/src/abi/cairo_types/error.rs create mode 100644 starknet-contract/src/abi/cairo_types/mod.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/array.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/boolean.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/felt.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/integers.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/mod.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/option.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/result.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/starknet.rs create mode 100644 starknet-contract/src/abi/cairo_types/types/tuple.rs create mode 100644 starknet-contract/src/abi/mod.rs create mode 100644 starknet-contract/src/abi/parser/abi_types/array.rs create mode 100644 starknet-contract/src/abi/parser/abi_types/basic.rs create mode 100644 starknet-contract/src/abi/parser/abi_types/generic.rs create mode 100644 starknet-contract/src/abi/parser/abi_types/mod.rs create mode 100644 starknet-contract/src/abi/parser/abi_types/tuple.rs create mode 100644 starknet-contract/src/abi/parser/cairo_enum.rs create mode 100644 starknet-contract/src/abi/parser/cairo_event.rs create mode 100644 starknet-contract/src/abi/parser/cairo_function.rs create mode 100644 starknet-contract/src/abi/parser/cairo_struct.rs create mode 100644 starknet-contract/src/abi/parser/mod.rs diff --git a/starknet-contract/src/abi/cairo_types/error.rs b/starknet-contract/src/abi/cairo_types/error.rs new file mode 100644 index 00000000..7ab1bf8a --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/error.rs @@ -0,0 +1,31 @@ +use crate::abi::cairo_types::CairoType; + +use starknet_core::types::FieldElement; + +/// Cairo types result. +pub type Result = core::result::Result; + +/// A cairo type error. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + /// Invalid type string. + InvalidTypeString(String), + /// Error during serialization. + Serialize(String), + /// Error during deserialization. + Deserialize(String), +} + +impl CairoType for Error { + type RustType = Self; + + fn serialize(_rust: &Self::RustType) -> Vec { + vec![] + } + + fn deserialize(_felts: &[FieldElement], _offset: usize) -> Result { + Ok(Error::Deserialize( + "Error cairotype deserialized?".to_string(), + )) + } +} diff --git a/starknet-contract/src/abi/cairo_types/mod.rs b/starknet-contract/src/abi/cairo_types/mod.rs new file mode 100644 index 00000000..239ff626 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/mod.rs @@ -0,0 +1,52 @@ +//! This crate contains the definition of traits and types +//! that map to Cairo types. +//! +//! Some of the Cairo types are provided in the ABI event if they are very generic +//! like `Option`, `Result`, etc... +//! This crate provides the `CairoType` implementation for those types and all basic +//! types from Cairo (integers, felt etc...). +//! +mod error; +pub use error::{Error, Result}; + +mod types; + +use starknet_core::types::FieldElement; + +/// Basic cairo structs that are already implemented inside +/// this crate and hence skipped during ABI generation. +pub const CAIRO_BASIC_STRUCTS: [&str; 4] = ["Span", "ClassHash", "ContractAddress", "EthAddress"]; + +/// Same as `CAIRO_BASIC_STRUCTS`, but for enums. +pub const CAIRO_BASIC_ENUMS: [&str; 2] = ["Option", "Result"]; + +/// CairoType trait to implement in order to serialize/deserialize +/// a Rust type to/from a CairoType. +pub trait CairoType { + /// The corresponding Rust type. + type RustType; + + /// The serialized size of the type in felts, if known at compile time. + const SERIALIZED_SIZE: Option = Some(1); + + /// Whether the serialized size is dynamic. + const DYNAMIC: bool = Self::SERIALIZED_SIZE.is_none(); + + /// Calculates the serialized size of the data for a single felt + /// it will always be 1. + /// If the type is dynamic, SERIALIZED_SIZE is None, but this + /// function is overriden to correctly compute the size. + #[inline] + fn serialized_size(_rust: &Self::RustType) -> usize { + Self::SERIALIZED_SIZE.unwrap() + } + + /// Serializes the given type into a FieldElement sequence. + fn serialize(rust: &Self::RustType) -> Vec; + + /// TODO: add serialize_to(rust: &Self::RustType, out: &mut Vec) + /// for large buffers optimization. + + /// Deserializes an array of felts into the given type. + fn deserialize(felts: &[FieldElement], offset: usize) -> Result; +} diff --git a/starknet-contract/src/abi/cairo_types/types/array.rs b/starknet-contract/src/abi/cairo_types/types/array.rs new file mode 100644 index 00000000..6d513d55 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/array.rs @@ -0,0 +1,131 @@ +//! CairoType implementation for `Vec`. +//! They are used for Array and Span cairo types. +use crate::abi::cairo_types::{CairoType, Error, Result}; +use starknet_core::types::FieldElement; + +impl CairoType for Vec +where + T: CairoType, +{ + type RustType = Vec; + + const SERIALIZED_SIZE: Option = None; + + #[inline] + fn serialized_size(rust: &Self::RustType) -> usize { + let data = rust; + // 1 + because the length is always the first felt. + 1 + data.iter().map(T::serialized_size).sum::() + } + + fn serialize(rust: &Self::RustType) -> Vec { + let mut out: Vec = vec![rust.len().into()]; + rust.iter().for_each(|r| out.extend(T::serialize(r))); + out + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + let len: usize = usize::from_str_radix(format!("{:x}", felts[offset]).as_str(), 16) + .map_err(|_| { + Error::Deserialize("First felt of an array must fit into usize".to_string()) + })?; + + let mut out: Vec = vec![]; + let mut offset = offset + 1; + + loop { + if out.len() == len { + break; + } + + let rust: RT = T::deserialize(felts, offset)?; + offset += T::serialized_size(&rust); + out.push(rust); + } + + Ok(out) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_array() { + let v: Vec = vec![1, 2, 3]; + let felts = Vec::::serialize(&v); + assert_eq!(felts.len(), 4); + assert_eq!(felts[0], FieldElement::from(3_u32)); + assert_eq!(felts[1], FieldElement::ONE); + assert_eq!(felts[2], FieldElement::TWO); + assert_eq!(felts[3], FieldElement::THREE); + } + + #[test] + fn test_deserialize_array() { + let felts: Vec = vec![ + FieldElement::from(2_u32), + FieldElement::from(123_u32), + FieldElement::from(9988_u32), + ]; + + let vals = Vec::::deserialize(&felts, 0).unwrap(); + assert_eq!(vals.len(), 2); + assert_eq!(vals[0], 123_u32); + assert_eq!(vals[1], 9988_u32); + } + + #[test] + fn test_serialize_array_nested() { + let v: Vec> = vec![vec![1, 2], vec![3]]; + let felts = Vec::>::serialize(&v); + assert_eq!(felts.len(), 6); + assert_eq!(felts[0], FieldElement::TWO); + assert_eq!(felts[1], FieldElement::TWO); + assert_eq!(felts[2], FieldElement::ONE); + assert_eq!(felts[3], FieldElement::TWO); + assert_eq!(felts[4], FieldElement::ONE); + assert_eq!(felts[5], FieldElement::THREE); + } + + #[test] + fn test_deserialize_array_nested() { + let felts: Vec = vec![ + FieldElement::TWO, + FieldElement::TWO, + FieldElement::ONE, + FieldElement::TWO, + FieldElement::ONE, + FieldElement::THREE, + ]; + + let vals = Vec::>::deserialize(&felts, 0).unwrap(); + assert_eq!(vals.len(), 2); + assert_eq!(vals[0], vec![1, 2]); + assert_eq!(vals[1], vec![3]); + } + + #[test] + fn test_serialize_array_tuple() { + let v: Vec<(u32, FieldElement)> = vec![(12, FieldElement::TWO)]; + let felts = Vec::<(u32, FieldElement)>::serialize(&v); + assert_eq!(felts.len(), 3); + assert_eq!(felts[0], FieldElement::from(1_u32)); + assert_eq!(felts[1], FieldElement::from(12_u32)); + assert_eq!(felts[2], FieldElement::TWO); + } + + #[test] + fn test_deserialize_array_tuple() { + let felts: Vec = vec![ + FieldElement::from(1_u32), + FieldElement::from(12_u32), + FieldElement::TWO, + ]; + + let vals = Vec::<(u32, FieldElement)>::deserialize(&felts, 0).unwrap(); + assert_eq!(vals.len(), 1); + assert_eq!(vals[0], (12, FieldElement::TWO)); + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/boolean.rs b/starknet-contract/src/abi/cairo_types/types/boolean.rs new file mode 100644 index 00000000..f3b58286 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/boolean.rs @@ -0,0 +1,45 @@ +//! CairoType implementation for bool. +use crate::abi::cairo_types::{CairoType, Result}; +use starknet_core::types::FieldElement; + +impl CairoType for bool { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + vec![FieldElement::from(*rust as u32)] + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + if felts[offset] == FieldElement::ONE { + Ok(true) + } else { + Ok(false) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_bool() { + let v = true; + let felts = bool::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::ONE); + + let v = false; + let felts = bool::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::ZERO); + } + + #[test] + fn test_deserialize_bool() { + let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; // allocating in the heap. + assert_eq!(bool::deserialize(&felts, 0).unwrap(), false); + assert_eq!(bool::deserialize(&felts, 1).unwrap(), true); + assert_eq!(bool::deserialize(&felts, 2).unwrap(), false); + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/felt.rs b/starknet-contract/src/abi/cairo_types/types/felt.rs new file mode 100644 index 00000000..aca6f184 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/felt.rs @@ -0,0 +1,56 @@ +use crate::abi::cairo_types::{CairoType, Result}; +use starknet_core::types::FieldElement; + +impl CairoType for () { + type RustType = Self; + + fn serialize(_rust: &Self::RustType) -> Vec { + vec![] + } + + fn deserialize(_felts: &[FieldElement], _offset: usize) -> Result { + Ok(()) + } +} + +impl CairoType for FieldElement { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + vec![*rust] + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + Ok(felts[offset]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_field_element() { + let f = FieldElement::ZERO; + let felts = FieldElement::serialize(&f); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::ZERO); + } + + #[test] + fn test_deserialize_field_element() { + let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; // allocating in the heap. + assert_eq!( + FieldElement::deserialize(&felts, 0).unwrap(), + FieldElement::ZERO + ); + assert_eq!( + FieldElement::deserialize(&felts, 1).unwrap(), + FieldElement::ONE + ); + assert_eq!( + FieldElement::deserialize(&felts, 2).unwrap(), + FieldElement::TWO + ); + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/integers.rs b/starknet-contract/src/abi/cairo_types/types/integers.rs new file mode 100644 index 00000000..71b62ee8 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/integers.rs @@ -0,0 +1,227 @@ +//! CairoType implementation for integers (signed/unsigned). +use crate::abi::cairo_types::{CairoType, Result}; +use starknet_core::types::FieldElement; + +macro_rules! implement_trait_for_unsigned { + ($type:ty) => { + impl CairoType for $type { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + vec![FieldElement::from(*rust)] + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + let temp: u128 = felts[offset].try_into().unwrap(); + Ok(temp as $type) + } + } + }; +} + +macro_rules! implement_trait_for_signed { + ($type:ty) => { + impl CairoType for $type { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + vec![FieldElement::from(*rust as usize)] + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + let temp: u128 = felts[offset].try_into().unwrap(); + Ok(temp as $type) + } + } + }; +} + +implement_trait_for_unsigned!(u8); +implement_trait_for_unsigned!(u16); +implement_trait_for_unsigned!(u32); +implement_trait_for_unsigned!(u64); +implement_trait_for_unsigned!(u128); +implement_trait_for_unsigned!(usize); + +implement_trait_for_signed!(i8); +implement_trait_for_signed!(i16); +implement_trait_for_signed!(i32); +implement_trait_for_signed!(i64); +implement_trait_for_signed!(i128); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_u8() { + let v = 12_u8; + let felts = u8::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(12 as u8)); + } + + #[test] + fn test_deserialize_u8() { + let felts = vec![FieldElement::from(12_u8), FieldElement::from(10_u8)]; + assert_eq!(u8::deserialize(&felts, 0).unwrap(), 12); + assert_eq!(u8::deserialize(&felts, 1).unwrap(), 10); + } + + #[test] + fn test_serialize_u16() { + let v = 12_u16; + let felts = u16::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(12 as u16)); + } + + #[test] + fn test_deserialize_u16() { + let felts = vec![FieldElement::from(12_u16), FieldElement::from(10_u8)]; + assert_eq!(u16::deserialize(&felts, 0).unwrap(), 12); + assert_eq!(u16::deserialize(&felts, 1).unwrap(), 10); + } + + #[test] + fn test_serialize_u32() { + let v = 123_u32; + let felts = u32::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(123 as u32)); + } + + #[test] + fn test_deserialize_u32() { + let felts = vec![FieldElement::from(123_u32), FieldElement::from(99_u32)]; + assert_eq!(u32::deserialize(&felts, 0).unwrap(), 123); + assert_eq!(u32::deserialize(&felts, 1).unwrap(), 99); + } + + #[test] + fn test_serialize_u64() { + let v = 123_u64; + let felts = u64::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(123 as u64)); + } + + #[test] + fn test_deserialize_u64() { + let felts = vec![FieldElement::from(123_u64), FieldElement::from(99_u64)]; + assert_eq!(u64::deserialize(&felts, 0).unwrap(), 123); + assert_eq!(u64::deserialize(&felts, 1).unwrap(), 99); + } + + #[test] + fn test_serialize_u128() { + let v = 123_u128; + let felts = u128::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(123 as u128)); + } + + #[test] + fn test_deserialize_u128() { + let felts = vec![FieldElement::from(123_u128), FieldElement::from(99_u128)]; + assert_eq!(u128::deserialize(&felts, 0).unwrap(), 123); + assert_eq!(u128::deserialize(&felts, 1).unwrap(), 99); + } + + #[test] + fn test_serialize_usize() { + let v = 123; + let felts = usize::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(123 as u128)); + } + + #[test] + fn test_deserialize_usize() { + let felts = vec![FieldElement::from(123_u128), FieldElement::from(99_u64)]; + assert_eq!(usize::deserialize(&felts, 0).unwrap(), 123); + assert_eq!(usize::deserialize(&felts, 1).unwrap(), 99); + } + + #[test] + fn test_serialize_i8() { + let v = i8::MAX; + let felts = i8::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(i8::MAX as u8)); + } + + #[test] + fn test_deserialize_i8() { + let felts = vec![ + FieldElement::from(i8::MAX as u8), + FieldElement::from(i8::MAX as u8), + ]; + assert_eq!(i8::deserialize(&felts, 0).unwrap(), i8::MAX); + assert_eq!(i8::deserialize(&felts, 1).unwrap(), i8::MAX); + } + + #[test] + fn test_serialize_i16() { + let v = i16::MAX; + let felts = i16::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(i16::MAX as u16)); + } + + #[test] + fn test_deserialize_i16() { + let felts = vec![ + FieldElement::from(i16::MAX as u16), + FieldElement::from(i16::MAX as u16), + ]; + assert_eq!(i16::deserialize(&felts, 0).unwrap(), i16::MAX); + assert_eq!(i16::deserialize(&felts, 1).unwrap(), i16::MAX); + } + + #[test] + fn test_serialize_i32() { + let v = i32::MAX; + let felts = i32::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(i32::MAX as u32)); + } + + #[test] + fn test_deserialize_i32() { + let felts = vec![ + FieldElement::from(i32::MAX as u32), + FieldElement::from(i32::MAX as u32), + ]; + assert_eq!(i32::deserialize(&felts, 0).unwrap(), i32::MAX); + assert_eq!(i32::deserialize(&felts, 1).unwrap(), i32::MAX); + } + + #[test] + fn test_serialize_i64() { + let v = i64::MAX; + let felts = i64::serialize(&v); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(i64::MAX as u64)); + } + + #[test] + fn test_deserialize_i64() { + let felts = vec![ + FieldElement::from(i64::MAX as u64), + FieldElement::from(i64::MAX as u64), + ]; + assert_eq!(i64::deserialize(&felts, 0).unwrap(), i64::MAX); + assert_eq!(i64::deserialize(&felts, 1).unwrap(), i64::MAX); + } + + #[test] + fn test_deserialize_i128() { + let felts = vec![ + FieldElement::from(i128::MAX as u128), + FieldElement::from(i128::MAX as u128), + ]; + assert_eq!(i128::deserialize(&felts, 0).unwrap(), i128::MAX); + assert_eq!(i128::deserialize(&felts, 1).unwrap(), i128::MAX); + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/mod.rs b/starknet-contract/src/abi/cairo_types/types/mod.rs new file mode 100644 index 00000000..e0322114 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/mod.rs @@ -0,0 +1,8 @@ +mod array; +mod boolean; +mod felt; +mod integers; +mod option; +mod result; +mod starknet; +mod tuple; diff --git a/starknet-contract/src/abi/cairo_types/types/option.rs b/starknet-contract/src/abi/cairo_types/types/option.rs new file mode 100644 index 00000000..b060fe5e --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/option.rs @@ -0,0 +1,127 @@ +//! CairoType implementation for Option. +//! +//! In cairo, `Some` is the first field and `None` the second one. +//! To follow the serialization rule, `Some` has index 0, and `None` index 1. +//! +//! https://github.com/starkware-libs/cairo/blob/main/corelib/src/option.cairo#L6 +use crate::abi::cairo_types::{CairoType, Error, Result}; +use starknet_core::types::FieldElement; + +impl CairoType for Option +where + T: CairoType, +{ + type RustType = Option; + + fn serialize(rust: &Self::RustType) -> Vec { + let mut out = vec![]; + + match rust { + Some(r) => { + out.push(FieldElement::ZERO); + out.extend(T::serialize(r)); + } + None => out.push(FieldElement::ONE), + }; + + out + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + let idx = felts[offset]; + + if idx == FieldElement::ZERO { + // + 1 as the offset value is the index of the enum. + Ok(Option::Some(T::deserialize(felts, offset + 1)?)) + } else if idx == FieldElement::ONE { + Ok(Option::None) + } else { + Err(Error::Deserialize( + "Option is expected 0 or 1 index only".to_string(), + )) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec::Vec; + use starknet::core::types::FieldElement; + + #[test] + fn test_option_some_serialize() { + let o = Some(u32::MAX); + let felts = Option::::serialize(&o); + assert_eq!(felts.len(), 2); + assert_eq!(felts[0], FieldElement::ZERO); + assert_eq!(felts[1], FieldElement::from(u32::MAX)); + } + + #[test] + fn test_option_some_deserialize() { + let felts = vec![FieldElement::ZERO, FieldElement::from(u32::MAX)]; + let o = Option::::deserialize(&felts, 0).unwrap(); + assert_eq!(o, Some(u32::MAX)); + + let felts = vec![ + FieldElement::THREE, + FieldElement::ZERO, + FieldElement::from(u32::MAX), + ]; + let o = Option::::deserialize(&felts, 1).unwrap(); + assert_eq!(o, Some(u32::MAX)); + } + + #[test] + fn test_option_some_array_serialize() { + let o = Some(vec![u32::MAX, u32::MAX]); + let felts = Option::>::serialize(&o); + assert_eq!(felts.len(), 4); + assert_eq!(felts[0], FieldElement::ZERO); + assert_eq!(felts[1], FieldElement::from(2_u32)); + assert_eq!(felts[2], FieldElement::from(u32::MAX)); + assert_eq!(felts[3], FieldElement::from(u32::MAX)); + } + + #[test] + fn test_option_some_array_deserialize() { + let felts = vec![ + FieldElement::ZERO, + FieldElement::from(2_u32), + FieldElement::from(u32::MAX), + FieldElement::from(u32::MAX), + ]; + let o = Option::>::deserialize(&felts, 0).unwrap(); + assert_eq!(o, Some(vec![u32::MAX, u32::MAX])); + + let felts = vec![ + FieldElement::THREE, + FieldElement::ZERO, + FieldElement::from(2_u32), + FieldElement::from(u32::MAX), + FieldElement::from(u32::MAX), + ]; + let o = Option::>::deserialize(&felts, 1).unwrap(); + assert_eq!(o, Some(vec![u32::MAX, u32::MAX])); + } + + #[test] + fn test_option_none_serialize() { + let o: Option = None; + let felts = Option::::serialize(&o); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::ONE); + } + + #[test] + fn test_option_none_deserialize() { + let felts = vec![FieldElement::ONE]; + let o = Option::::deserialize(&felts, 0).unwrap(); + assert_eq!(o, None); + + let felts = vec![FieldElement::THREE, FieldElement::ONE]; + let o = Option::::deserialize(&felts, 1).unwrap(); + assert_eq!(o, None); + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/result.rs b/starknet-contract/src/abi/cairo_types/types/result.rs new file mode 100644 index 00000000..53bff34b --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/result.rs @@ -0,0 +1,98 @@ +//! CairoType implementation for Result. +//! +//! https://github.com/starkware-libs/cairo/blob/main/corelib/src/result.cairo#L6 +use crate::abi::cairo_types::{CairoType, Error as CairoError, Result as CairoResult}; +use starknet_core::types::FieldElement; + +impl CairoType for Result +where + T: CairoType, + E: CairoType, +{ + type RustType = Result; + + fn serialize(rust: &Self::RustType) -> Vec { + let mut out = vec![]; + + match rust { + Result::Ok(r) => { + out.push(FieldElement::ZERO); + out.extend(T::serialize(r)); + } + Result::Err(e) => { + out.push(FieldElement::ONE); + out.extend(E::serialize(e)); + } + }; + + out + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> CairoResult { + let idx = felts[offset]; + + if idx == FieldElement::ZERO { + // + 1 as the offset value is the index of the enum. + CairoResult::Ok(Ok(T::deserialize(felts, offset + 1)?)) + } else if idx == FieldElement::ONE { + CairoResult::Ok(Err(E::deserialize(felts, offset + 1)?)) + } else { + Err(CairoError::Deserialize( + "Result is expected 0 or 1 index only".to_string(), + )) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use starknet::core::types::FieldElement; + + #[test] + fn test_result_ok_serialize() { + let r = Ok(u32::MAX); + let felts = Result::::serialize(&r); + assert_eq!(felts.len(), 2); + assert_eq!(felts[0], FieldElement::ZERO); + assert_eq!(felts[1], FieldElement::from(u32::MAX)); + } + + #[test] + fn test_result_ok_deserialize() { + let felts = vec![FieldElement::ZERO, FieldElement::from(u32::MAX)]; + let r = Result::::deserialize(&felts, 0).unwrap(); + assert_eq!(r, Ok(u32::MAX)); + } + + #[test] + fn test_result_ok_unit_serialize() { + let r = Ok(()); + let felts = Result::<(), FieldElement>::serialize(&r); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::ZERO); + } + + #[test] + fn test_result_ok_unit_deserialize() { + let felts = vec![FieldElement::ZERO]; + let r = Result::<(), FieldElement>::deserialize(&felts, 0).unwrap(); + assert_eq!(r, Ok(())); + } + + #[test] + fn test_result_err_serialize() { + let r = Err(FieldElement::ONE); + let felts = Result::::serialize(&r); + assert_eq!(felts.len(), 2); + assert_eq!(felts[0], FieldElement::ONE); + assert_eq!(felts[1], FieldElement::ONE); + } + + #[test] + fn test_result_err_deserialize() { + let felts = vec![FieldElement::ONE, FieldElement::ONE]; + let r = Result::::deserialize(&felts, 0).unwrap(); + assert_eq!(r, Err(FieldElement::ONE)); + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/starknet.rs b/starknet-contract/src/abi/cairo_types/types/starknet.rs new file mode 100644 index 00000000..f191f682 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/starknet.rs @@ -0,0 +1,139 @@ +//! CairoType implementation for starknet types. +//! +//! They are alf `FieldElement` under the hood. +use crate::abi::cairo_types::{CairoType, Result}; +use starknet_core::types::FieldElement; + +/// ContractAddress. +#[derive(Debug, PartialEq)] +pub struct ContractAddress(pub FieldElement); + +impl From for ContractAddress { + fn from(item: FieldElement) -> Self { + Self(item) + } +} + +impl CairoType for ContractAddress { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + FieldElement::serialize(&rust.0) + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + Ok(ContractAddress(FieldElement::deserialize(felts, offset)?)) + } +} + +/// ClassHash. +#[derive(Debug, PartialEq)] +pub struct ClassHash(pub FieldElement); + +impl From for ClassHash { + fn from(item: FieldElement) -> Self { + Self(item) + } +} + +impl CairoType for ClassHash { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + FieldElement::serialize(&rust.0) + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + Ok(ClassHash(FieldElement::deserialize(felts, offset)?)) + } +} + +/// EthAddress. +#[derive(Debug, PartialEq)] +pub struct EthAddress(pub FieldElement); + +impl From for EthAddress { + fn from(item: FieldElement) -> Self { + Self(item) + } +} + +impl CairoType for EthAddress { + type RustType = Self; + + fn serialize(rust: &Self::RustType) -> Vec { + FieldElement::serialize(&rust.0) + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + Ok(EthAddress(FieldElement::deserialize(felts, offset)?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_contract_address_serialize() { + let contract_address = ContractAddress(FieldElement::from(1_u32)); + let felts = ContractAddress::serialize(&contract_address); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(1_u32)); + } + + #[test] + fn test_contract_address_deserialize() { + let felts = vec![FieldElement::from(1_u32)]; + let contract_address = ContractAddress::deserialize(&felts, 0).unwrap(); + assert_eq!(contract_address, ContractAddress(FieldElement::from(1_u32))) + } + + #[test] + fn test_class_hash_serialize() { + let class_hash = ClassHash(FieldElement::from(1_u32)); + let felts = ClassHash::serialize(&class_hash); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(1_u32)); + } + + #[test] + fn test_class_hash_deserialize() { + let felts = vec![FieldElement::from(1_u32)]; + let class_hash = ClassHash::deserialize(&felts, 0).unwrap(); + assert_eq!(class_hash, ClassHash(FieldElement::from(1_u32))) + } + + #[test] + fn test_eth_address_serialize() { + let eth_address = EthAddress(FieldElement::from(1_u32)); + let felts = EthAddress::serialize(ð_address); + assert_eq!(felts.len(), 1); + assert_eq!(felts[0], FieldElement::from(1_u32)); + } + + #[test] + fn test_eth_address_deserialize() { + let felts = vec![FieldElement::from(1_u32)]; + let eth_address = EthAddress::deserialize(&felts, 0).unwrap(); + assert_eq!(eth_address, EthAddress(FieldElement::from(1_u32))) + } + + #[test] + fn test_contract_address_from() { + let contract_address = ContractAddress::from(FieldElement::from(1_u32)); + assert_eq!(contract_address, ContractAddress(FieldElement::from(1_u32))) + } + + #[test] + fn test_class_hash_from() { + let class_hash = ClassHash::from(FieldElement::from(1_u32)); + assert_eq!(class_hash, ClassHash(FieldElement::from(1_u32))) + } + + #[test] + fn test_eth_address_from() { + let eth_address = EthAddress::from(FieldElement::from(1_u32)); + assert_eq!(eth_address, EthAddress(FieldElement::from(1_u32))) + } +} diff --git a/starknet-contract/src/abi/cairo_types/types/tuple.rs b/starknet-contract/src/abi/cairo_types/types/tuple.rs new file mode 100644 index 00000000..0ee448f9 --- /dev/null +++ b/starknet-contract/src/abi/cairo_types/types/tuple.rs @@ -0,0 +1,93 @@ +//! CairoType implementation for tuples. +use crate::abi::cairo_types::{CairoType, Result}; +use starknet_core::types::FieldElement; + +macro_rules! impl_tuples { + ($num:expr, $( $ty:ident : $rt:ident : $var:ident : $no:tt ),+ $(,)?) => { + impl<$( $ty, $rt ),+> CairoType for ($( $ty, )+) + where + $($ty: CairoType,)+ + { + type RustType = ($( $rt ),*); + + const SERIALIZED_SIZE: Option = None; + + #[inline] + fn serialized_size(rust: &Self::RustType) -> usize { + let mut size = 0; + $( + size += $ty::serialized_size(& rust.$no); + )* + + size + } + + fn serialize(rust: &Self::RustType) -> Vec { + let mut out: Vec = vec![]; + + $( out.extend($ty::serialize(& rust.$no)); )* + + out + } + + fn deserialize(felts: &[FieldElement], offset: usize) -> Result { + let mut offset = offset; + + $( + let $var : $rt = $ty::deserialize(felts, offset)?; + offset += $ty::serialized_size(& $var); + )* + + // Remove warning. + let _offset = offset; + + Ok(($( $var ),*)) + } + } + } +} + +impl_tuples!(2, A:RA:r0:0, B:RB:r1:1); +impl_tuples!(3, A:RA:r0:0, B:RB:r1:1, C:RC:r2:2); +impl_tuples!(4, A:RA:r0:0, B:RB:r1:1, C:RC:r2:2, D:RD:r3:3); +impl_tuples!(5, A:RA:r0:0, B:RB:r1:1, C:RC:r2:2, D:RD:r3:3, E:RE:r4:4); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_tuple2() { + let v = (FieldElement::ONE, 128_u32); + let felts = <(FieldElement, u32)>::serialize(&v); + assert_eq!(felts.len(), 2); + assert_eq!(felts[0], FieldElement::ONE); + assert_eq!(felts[1], FieldElement::from(128_u32)); + } + + #[test] + fn test_deserialize_tuple2() { + let felts = vec![FieldElement::THREE, 99_u32.into()]; + let vals = <(FieldElement, u32)>::deserialize(&felts, 0).unwrap(); + assert_eq!(vals.0, FieldElement::THREE); + assert_eq!(vals.1, 99_u32); + } + + #[test] + fn test_serialize_tuple2_array() { + let v = (vec![FieldElement::ONE], 128_u32); + let felts = <(Vec, u32)>::serialize(&v); + assert_eq!(felts.len(), 3); + assert_eq!(felts[0], FieldElement::ONE); + assert_eq!(felts[1], FieldElement::ONE); + assert_eq!(felts[2], FieldElement::from(128_u32)); + } + + #[test] + fn test_deserialize_tuple2_array() { + let felts = vec![FieldElement::ONE, FieldElement::ONE, 99_u32.into()]; + let vals = <(Vec, u32)>::deserialize(&felts, 0).unwrap(); + assert_eq!(vals.0, vec![FieldElement::ONE]); + assert_eq!(vals.1, 99_u32); + } +} diff --git a/starknet-contract/src/abi/mod.rs b/starknet-contract/src/abi/mod.rs new file mode 100644 index 00000000..1b631d25 --- /dev/null +++ b/starknet-contract/src/abi/mod.rs @@ -0,0 +1,2 @@ +mod cairo_types; +mod parser; diff --git a/starknet-contract/src/abi/parser/abi_types/array.rs b/starknet-contract/src/abi/parser/abi_types/array.rs new file mode 100644 index 00000000..5a23e038 --- /dev/null +++ b/starknet-contract/src/abi/parser/abi_types/array.rs @@ -0,0 +1,189 @@ +use super::{AbiType, AbiTypeAny, GENTY_FROZEN}; + +#[derive(Debug, PartialEq, Clone)] +pub struct AbiArray { + pub cairo_type: String, + pub genty: String, + pub inner: Box, +} + +impl AbiArray { + pub fn new(cairo_type: &str, inner: AbiTypeAny) -> Self { + AbiArray { + cairo_type: cairo_type.to_string(), + genty: String::new(), + inner: Box::new(inner), + } + } +} + +impl AbiType for AbiArray { + fn get_genty(&self) -> String { + self.genty.clone() + } + + fn compare_generic(&mut self, other: &AbiTypeAny) { + match other { + AbiTypeAny::Array(_) => { + if self.genty != GENTY_FROZEN { + self.genty = other.get_genty(); + } + } + _ => { + self.inner.compare_generic(other); + } + }; + } + + fn apply_generic(&mut self, cairo_types_gentys: Vec<(&str, &str)>) -> (String, bool) { + // Check if the whole array is the generic. + for (cairo_type, genty) in &cairo_types_gentys { + if &self.get_cairo_type_full() == cairo_type { + self.genty = genty.to_string(); + return (genty.to_string(), true); + } + } + + let (gen_str, is_generic) = self.inner.apply_generic(cairo_types_gentys); + ( + format!("{}::<{}>", self.cairo_type.clone(), &gen_str), + is_generic, + ) + } + + fn get_cairo_type_full(&self) -> String { + format!( + "{}::<{}>", + self.cairo_type.clone(), + &self.inner.get_cairo_type_full() + ) + } + + fn get_cairo_type_name(&self) -> String { + self.cairo_type + .split("::") + .last() + .unwrap_or(&self.cairo_type) + .to_string() + } + + fn to_rust_type(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + format!("Vec<{}>", &self.inner.to_rust_type()) + } + } + + fn to_rust_type_path(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + format!("Vec::<{}>", &self.inner.to_rust_type()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi_types::{AbiBasic, AbiTypeAny}; + + fn get_default() -> AbiArray { + AbiArray::new( + "core::array::Array", + AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), + ) + } + + #[test] + fn get_cairo_type_full() { + let t = get_default(); + assert_eq!( + t.get_cairo_type_full(), + "core::array::Array::" + ); + } + + #[test] + fn cairo_type_name_only() { + let t = get_default(); + assert_eq!(t.get_cairo_type_name(), "Array"); + } + + #[test] + fn to_rust_type() { + let t = get_default(); + assert_eq!(t.to_rust_type(), "Vec"); + } + + #[test] + fn to_rust_type_path() { + let t = get_default(); + assert_eq!( + t.to_rust_type_path(), + "Vec::" + ); + } + + #[test] + fn from_string() { + let t = AbiTypeAny::from_string("core::array::Array::"); + assert_eq!(t, AbiTypeAny::Array(get_default())); + } + + #[test] + fn from_string_array_tuple() { + let t = + AbiTypeAny::from_string("core::array::Array::<(core::felt252, core::integer::u32)>"); + assert_eq!( + t, + AbiTypeAny::Array(AbiArray::new( + "core::array::Array", + AbiTypeAny::Tuple( + vec![ + AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), + AbiTypeAny::Basic(AbiBasic::new("core::integer::u32".into())), + ] + .into() + ) + )) + ); + } + + #[test] + fn generic_array() { + let mut t = AbiTypeAny::from_string("core::array::Array::"); + assert_eq!( + t.apply_generic(vec![("core::array::Array::", "A")]), + ("A".to_string(), true) + ); + } + + #[test] + fn generic_inner() { + let mut t = AbiTypeAny::from_string("core::array::Array::"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ("core::array::Array::".to_string(), true) + ); + } + + #[test] + fn generic_not() { + let mut t = AbiTypeAny::from_string("core::array::Array::"); + assert_eq!( + t.apply_generic(vec![("core::array::Array", "A")]), + ("core::array::Array::".to_string(), false) + ); + } + + #[test] + fn generic_not_inner() { + let mut t = AbiTypeAny::from_string("core::array::Array::"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ("core::array::Array::".to_string(), false) + ); + } +} diff --git a/starknet-contract/src/abi/parser/abi_types/basic.rs b/starknet-contract/src/abi/parser/abi_types/basic.rs new file mode 100644 index 00000000..fb36cb32 --- /dev/null +++ b/starknet-contract/src/abi/parser/abi_types/basic.rs @@ -0,0 +1,156 @@ +//! Basic types are all cairo types that are not Array/Span, +//! generic Struct/Enum or tuple. +//! +//! To support recursion, the basic type stored the generic type +//! that is assigned to it, if it belongs to a generic struct/enum. +use super::{AbiType, AbiTypeAny, GENTY_FROZEN}; + +#[derive(Debug, PartialEq, Clone)] +pub struct AbiBasic { + cairo_type: String, + genty: String, +} + +impl AbiBasic { + /// Initializes a new instance. + pub fn new(cairo_type: &str) -> Self { + AbiBasic { + cairo_type: cairo_type.to_string(), + genty: String::new(), + } + } + + /// Maps a basic type to a built-in type that may already contains + /// a `CairoType` implementation. If not, it's the name of the type itself. + fn to_rust_or_cairo_builtin_type(&self) -> String { + let s = self.get_cairo_type_name(); + match s.as_str() { + "felt252" => "starknet::core::types::FieldElement".to_string(), + "ContractAddress" => "cairo_types::types::starknet::ContractAddress".to_string(), + "ClassHash" => "cairo_types::types::starknet::ClassHash".to_string(), + "EthAddress" => "cairo_types::types::starknet::EthAddress".to_string(), + _ => s.clone(), + } + } +} + +impl From<&str> for AbiBasic { + fn from(s: &str) -> Self { + Self::new(s) + } +} + +impl From<&String> for AbiBasic { + fn from(s: &String) -> Self { + Self::new(s) + } +} + +impl AbiType for AbiBasic { + fn get_genty(&self) -> String { + self.genty.clone() + } + + fn compare_generic(&mut self, other: &AbiTypeAny) { + if self.genty != GENTY_FROZEN { + self.genty = other.get_genty(); + } + } + + fn apply_generic(&mut self, cairo_types_gentys: Vec<(&str, &str)>) -> (String, bool) { + // A basic type can only match one of the given types. + // It will return the first match we can find, if any. + for (cairo_type, genty) in cairo_types_gentys { + if self.cairo_type == cairo_type { + self.genty = genty.to_string(); + return (genty.to_string(), true); + } + } + + self.genty = GENTY_FROZEN.to_string(); + (self.cairo_type.clone(), false) + } + + fn get_cairo_type_full(&self) -> String { + self.cairo_type.clone() + } + + fn get_cairo_type_name(&self) -> String { + self.cairo_type + .split("::") + .last() + .unwrap_or(&self.cairo_type) + .to_string() + } + + fn to_rust_type(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + self.to_rust_or_cairo_builtin_type() + } + } + + fn to_rust_type_path(&self) -> String { + self.to_rust_type() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi_types::AbiTypeAny; + + fn get_default() -> AbiBasic { + AbiBasic::new("core::felt252") + } + + #[test] + fn get_cairo_type_full() { + let t = get_default(); + assert_eq!(t.get_cairo_type_full(), "core::felt252"); + } + + #[test] + fn cairo_type_name_only() { + let t = get_default(); + assert_eq!(t.get_cairo_type_name(), "felt252"); + } + + #[test] + fn to_rust_type() { + let t = get_default(); + assert_eq!(t.to_rust_type(), "starknet::core::types::FieldElement"); + } + + #[test] + fn to_rust_type_path() { + let t = get_default(); + assert_eq!(t.to_rust_type_path(), "starknet::core::types::FieldElement"); + } + // TODO: add more tests for other built-in types. + + #[test] + fn from_string() { + let t = AbiTypeAny::from_string("core::felt252"); + assert_eq!(t, AbiTypeAny::Basic("core::felt252".into())); + } + + #[test] + fn from_string_generic() { + let mut t = AbiTypeAny::from_string("core::felt252"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ("A".to_string(), true) + ); + } + + #[test] + fn from_string_not_generic() { + let mut t = AbiTypeAny::from_string("core::u32"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ("core::u32".to_string(), false) + ); + } +} diff --git a/starknet-contract/src/abi/parser/abi_types/generic.rs b/starknet-contract/src/abi/parser/abi_types/generic.rs new file mode 100644 index 00000000..ba9e4291 --- /dev/null +++ b/starknet-contract/src/abi/parser/abi_types/generic.rs @@ -0,0 +1,355 @@ +use super::{AbiType, AbiTypeAny, GENTY_FROZEN}; + +#[derive(Debug, PartialEq, Clone)] +pub struct AbiGeneric { + pub cairo_type: String, + pub genty: String, + pub inners: Vec, +} + +impl AbiGeneric { + /// Initializes a new instance. + pub fn new(cairo_type: &str, inners: Vec) -> Self { + AbiGeneric { + cairo_type: cairo_type.to_string(), + genty: String::new(), + inners, + } + } + + /// Gets the definition of the type with it's generic types. + pub fn get_rust_generic_def(&self, suffix: &str) -> String { + let gentys = self.get_gentys_only(); + format!( + "{}<{}{}>", + self.get_cairo_type_name(), + gentys.join(", "), + suffix + ) + } + + /// Returns only the generic types list. + pub fn get_gentys_only(&self) -> Vec { + // Starts to 'A'. + let ascii: u8 = 65; + + let mut gentys = vec![]; + for (i, _) in self.inners.iter().enumerate() { + gentys.push(((ascii + i as u8) as char).to_string()); + } + + gentys + } + + /// Returns the list of tuple, containing the (cairo_type, generic_type) + /// for each generic type. + pub fn get_cairo_types_gentys(&self) -> Vec<(String, String)> { + // Starts to 'A'. + let ascii: u8 = 65; + + let mut cairo_types_gentys = vec![]; + for (i, inner) in self.inners.iter().enumerate() { + let genty = ((ascii + i as u8) as char).to_string(); + cairo_types_gentys.push((inner.get_cairo_type_full(), genty)); + } + + cairo_types_gentys + } +} + +impl AbiType for AbiGeneric { + fn get_genty(&self) -> String { + self.genty.clone() + } + + fn compare_generic(&mut self, other: &AbiTypeAny) { + match other { + AbiTypeAny::Generic(_) => { + if self.genty != GENTY_FROZEN { + self.genty = other.get_genty(); + } + } + _ => { + for inner in &mut self.inners { + inner.compare_generic(other); + } + } + }; + } + + fn apply_generic(&mut self, cairo_types_gentys: Vec<(&str, &str)>) -> (String, bool) { + // Check if the whole struct is the generic. + for (cairo_type, genty) in &cairo_types_gentys { + if &self.get_cairo_type_full() == cairo_type { + self.genty = genty.to_string(); + return (genty.to_string(), true); + } + } + + let mut struct_has_generic = false; + let mut s = format!("{}::<", self.cairo_type); + let arr_len = self.inners.len(); + + for (idx, inner) in self.inners.iter_mut().enumerate() { + let (type_str, is_generic) = inner.apply_generic(cairo_types_gentys.clone()); + + if is_generic && !struct_has_generic { + struct_has_generic = true; + } + + s.push_str(&type_str); + + if idx < arr_len - 1 { + s.push_str(", "); + } + } + s.push('>'); + + (s, struct_has_generic) + } + + fn get_cairo_type_full(&self) -> String { + let mut s = format!("{}::<", self.cairo_type); + + for (idx, inner) in self.inners.iter().enumerate() { + s.push_str(&inner.get_cairo_type_full()); + + if idx < self.inners.len() - 1 { + s.push_str(", "); + } + } + s.push('>'); + s + } + + fn get_cairo_type_name(&self) -> String { + // TODO: need to opti that with regex? + let f = self + .cairo_type + .split('<') + .nth(0) + .unwrap_or(&self.cairo_type) + .to_string(); + f.split("::").last().unwrap_or(&f).to_string() + } + + fn to_rust_type(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + let joined_inners = self + .inners + .iter() + .map(|i| i.to_rust_type()) + .collect::>() + .join(", "); + + format!("{}<{}>", self.get_cairo_type_name(), joined_inners) + } + } + + fn to_rust_type_path(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + let joined_inners = self + .inners + .iter() + .map(|i| i.to_rust_type()) + .collect::>() + .join(", "); + + format!("{}::<{}>", self.get_cairo_type_name(), joined_inners) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; + + fn get_default() -> AbiGeneric { + AbiGeneric::new( + "contract1::MyStruct", + vec![AbiTypeAny::Basic(AbiBasic::new("core::felt252".into()))], + ) + } + + fn get_default_multiple() -> AbiGeneric { + AbiGeneric::new( + "contract1::MyStruct", + vec![ + AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), + AbiTypeAny::Basic(AbiBasic::new("core::integer::u32".into())), + ], + ) + } + + #[test] + fn cairo_type() { + let t = get_default(); + assert_eq!(t.cairo_type, "contract1::MyStruct"); + } + + #[test] + fn get_cairo_type_full() { + let t = get_default(); + assert_eq!( + t.get_cairo_type_full(), + "contract1::MyStruct::" + ); + } + + #[test] + fn cairo_type_name_only() { + let t = get_default(); + assert_eq!(t.get_cairo_type_name(), "MyStruct"); + } + + #[test] + fn to_rust_type() { + let t = get_default(); + assert_eq!( + t.to_rust_type(), + "MyStruct" + ); + } + + #[test] + fn to_rust_type_path() { + let t = get_default(); + assert_eq!( + t.to_rust_type_path(), + "MyStruct::" + ); + } + + #[test] + fn from_string() { + let t = AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!(t, AbiTypeAny::Generic(get_default())); + } + + #[test] + fn from_string_array_tuple() { + let t = AbiTypeAny::from_string("contract1::MyStruct::, (core::felt252, core::integer::u32)>"); + assert_eq!( + t, + AbiTypeAny::Generic(AbiGeneric::new( + "contract1::MyStruct", + vec![ + AbiTypeAny::Array(AbiArray::new( + "core::array::Array", + AbiTypeAny::Basic("core::felt252".into()) + )), + AbiTypeAny::Tuple( + vec![ + AbiTypeAny::Basic("core::felt252".into()), + AbiTypeAny::Basic("core::integer::u32".into()), + ] + .into() + ) + ] + )) + ); + } + + #[test] + fn get_cairo_type_full_multiple() { + let t = get_default_multiple(); + assert_eq!( + t.get_cairo_type_full(), + "contract1::MyStruct::" + ); + } + + #[test] + fn to_rust_type_multiple() { + let t = get_default_multiple(); + assert_eq!( + t.to_rust_type(), + "MyStruct" + ); + } + + #[test] + fn to_rust_type_path_multiple() { + let t = get_default_multiple(); + assert_eq!( + t.to_rust_type_path(), + "MyStruct::" + ); + } + + #[test] + fn from_string_multiple() { + let t = AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!(t, AbiTypeAny::Generic(get_default_multiple())); + } + + #[test] + fn generic_generic() { + let mut t = AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!( + t.apply_generic(vec![("contract1::MyStruct::", "A")]), + ("A".to_string(), true) + ); + } + + #[test] + fn generic_inner() { + let mut t = AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ("contract1::MyStruct::".to_string(), true) + ); + } + + #[test] + fn generic_generic_multiple() { + let mut t = + AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!( + t.apply_generic(vec![( + "contract1::MyStruct::", + "A" + )]), + ("A".to_string(), true) + ); + } + + #[test] + fn generic_inner_multiple() { + let mut t = + AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!( + t.apply_generic(vec![("core::integer::u32", "A")]), + ("contract1::MyStruct::".to_string(), true) + ); + } + + #[test] + fn generic_inner_multiple_array() { + let mut t = AbiTypeAny::from_string( + "contract1::MyStruct::, core::integer::u32>", + ); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ( + "contract1::MyStruct::, core::integer::u32>".to_string(), + true + ) + ); + } + + #[test] + fn generic_inner_multiple_ab() { + let mut t = + AbiTypeAny::from_string("contract1::MyStruct::"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A"), ("core::integer::u32", "B")]), + ("contract1::MyStruct::".to_string(), true) + ); + } +} diff --git a/starknet-contract/src/abi/parser/abi_types/mod.rs b/starknet-contract/src/abi/parser/abi_types/mod.rs new file mode 100644 index 00000000..46d3d1c8 --- /dev/null +++ b/starknet-contract/src/abi/parser/abi_types/mod.rs @@ -0,0 +1,302 @@ +//! ABI types base module. +//! +//! The idea of those types is to handle the parsing of any valid +//! flatten cairo type that can also contain nested types. +use std::iter::Peekable; +use std::str::Chars; + +pub mod basic; +pub use basic::AbiBasic; + +pub mod array; +pub use array::AbiArray; + +pub mod generic; +pub use generic::AbiGeneric; + +pub mod tuple; +pub use tuple::AbiTuple; + +/// If a generic type is flagged as frozen, it means +/// that at least one type in the ABI were found +/// different from the generic one. In that case, we +/// don't want it to be modified again, even if it matches +/// an other generic type. +/// +/// # Example +/// +/// struct MyStruct { +/// a: felt252, +/// b: T, +/// } +/// +/// In this scenario, when `MyStruct` is used, +/// we can't know which of a or b is generic. So both will be which is fine. +/// But if in the same ABI we have `MyStruct`, then it will +/// be possible to detect that b is generic, a is not. +/// So if we parse `MyStruct` first, we do want to FREEZE a +/// as NOT being generic. Like this even if for `MyStruct` there is +/// a match, it will be ignored. +const GENTY_FROZEN: &str = "_"; + +#[derive(Debug, PartialEq, Clone)] +pub enum AbiTypeAny { + Basic(AbiBasic), + Array(AbiArray), + // Generics is for struct and enums. + Generic(AbiGeneric), + Tuple(AbiTuple), +} + +pub trait AbiType { + /// Gets the generic type if the type is generic, + /// the type name otherwise. + fn get_genty(&self) -> String; + + /// Compares the generic state between two `AbiTypeAny`. + /// As the ABI does not provide information about the type genericity, + /// we must compare several types with the same name to successfully identify + /// the one that are generic. + fn compare_generic(&mut self, other: &AbiTypeAny); + + /// Applies a generic type for the given cairo type Vec(cairo_type, generic_type). + /// Returns the generic type applied and true if the type is generic, + /// false and the type itself otherwise. + fn apply_generic(&mut self, cairo_types_gentys: Vec<(&str, &str)>) -> (String, bool); + + /// Gets the full cairo type. A "full" type includes the type + /// and possible nested types. + fn get_cairo_type_full(&self) -> String; + + /// Returns only the cairo type name. + fn get_cairo_type_name(&self) -> String; + + /// Gets the rust type from the `AbiType`. + /// This always includes all possible nested types and their genericity. + fn to_rust_type(&self) -> String; + + /// Get the rust type item path from the `AbiType`. + /// This always includes all possible nested types and their genericity. + fn to_rust_type_path(&self) -> String; +} + +impl AbiType for AbiTypeAny { + fn compare_generic(&mut self, other: &AbiTypeAny) { + match self { + AbiTypeAny::Basic(a) => a.compare_generic(other), + AbiTypeAny::Array(a) => a.compare_generic(other), + AbiTypeAny::Generic(a) => a.compare_generic(other), + AbiTypeAny::Tuple(a) => a.compare_generic(other), + } + } + + fn get_genty(&self) -> String { + match self { + AbiTypeAny::Basic(a) => a.get_genty(), + AbiTypeAny::Array(a) => a.get_genty(), + AbiTypeAny::Generic(a) => a.get_genty(), + AbiTypeAny::Tuple(a) => a.get_genty(), + } + } + + fn apply_generic(&mut self, cairo_types_gentys: Vec<(&str, &str)>) -> (String, bool) { + match self { + AbiTypeAny::Basic(a) => a.apply_generic(cairo_types_gentys), + AbiTypeAny::Array(a) => a.apply_generic(cairo_types_gentys), + AbiTypeAny::Generic(a) => a.apply_generic(cairo_types_gentys), + AbiTypeAny::Tuple(a) => a.apply_generic(cairo_types_gentys), + } + } + + fn get_cairo_type_full(&self) -> String { + match self { + AbiTypeAny::Basic(a) => a.get_cairo_type_full(), + AbiTypeAny::Array(a) => a.get_cairo_type_full(), + AbiTypeAny::Generic(a) => a.get_cairo_type_full(), + AbiTypeAny::Tuple(a) => a.get_cairo_type_full(), + } + } + + fn get_cairo_type_name(&self) -> String { + match self { + AbiTypeAny::Basic(a) => a.get_cairo_type_name(), + AbiTypeAny::Array(a) => a.get_cairo_type_name(), + AbiTypeAny::Generic(a) => a.get_cairo_type_name(), + AbiTypeAny::Tuple(a) => a.get_cairo_type_name(), + } + } + + fn to_rust_type(&self) -> String { + match self { + AbiTypeAny::Basic(a) => a.to_rust_type(), + AbiTypeAny::Array(a) => a.to_rust_type(), + AbiTypeAny::Generic(a) => a.to_rust_type(), + AbiTypeAny::Tuple(a) => a.to_rust_type(), + } + } + + fn to_rust_type_path(&self) -> String { + match self { + AbiTypeAny::Basic(a) => a.to_rust_type_path(), + AbiTypeAny::Array(a) => a.to_rust_type_path(), + AbiTypeAny::Generic(a) => a.to_rust_type_path(), + AbiTypeAny::Tuple(a) => a.to_rust_type_path(), + } + } +} + +/// Utils functions for `AbiTypeAny` to be called +/// without testing the enum variant. +impl AbiTypeAny { + /// Returns true if the type is a generic, + /// false otherwise. + pub fn is_generic(&self) -> bool { + matches!(self, Self::Generic(_)) + } + + /// Returns true if the type is the unit type, + /// false otherwise. + pub fn is_unit(&self) -> bool { + match self { + Self::Basic(b) => b.get_cairo_type_full() == "()", + _ => false, + } + } + + /// Parses a string to build an `AbiTypeAny`. + pub fn from_string(type_string: &str) -> Self { + let mut chars = type_string.chars().peekable(); + Self::parse_type(&mut chars) + } + + /// Parses any cairo type from the given string. + /// This function handles the possible nested types. + fn parse_type(chars: &mut Peekable) -> Self { + let mut generic_types = Vec::new(); + let mut current_type = String::new(); + let mut in_generic = false; + + while let Some(c) = chars.peek() { + match c { + '<' => { + chars.next(); + // In cairo, a generic type is always preceded by a separator "::". + let generic_type = + Self::parse_generic(current_type.trim_end_matches("::"), chars); + generic_types.push(generic_type); + in_generic = true; + current_type.clear(); + } + '>' => { + if in_generic { + chars.next(); + in_generic = false; + } else { + break; + } + } + '(' => { + chars.next(); + let tuple_type = Self::parse_tuple(chars); + generic_types.push(tuple_type); + } + ')' => { + break; + } + ',' => { + break; + } + ' ' => { + // Ignore white spaces. + chars.next(); + } + _ => { + current_type.push(*c); + chars.next(); + } + } + } + + if !current_type.is_empty() { + generic_types.push(AbiTypeAny::Basic((¤t_type).into())); + } + + if generic_types.is_empty() { + // TODO: check if this one may be handled as Basic("()"); + Self::Basic("()".into()) + } else if generic_types.len() == 1 { + // Basic, Array or Generic with 1 inner type. + generic_types.pop().unwrap() + } else if chars.nth(0) == Some('(') { + // Tuple. + Self::Tuple(AbiTuple::new(generic_types)) + } else { + unreachable!(); + } + } + + /// Parses generic types detected between angle brackets. + fn parse_generic(current_type: &str, chars: &mut Peekable) -> Self { + let mut inners = vec![]; + + while let Some(c) = chars.peek() { + match c { + '>' => { + chars.next(); + break; + } + ',' => { + chars.next(); + inners.push(Self::parse_type(chars)) + } + _ => { + inners.push(Self::parse_type(chars)); + } + } + } + + if inners.is_empty() { + panic!("Array/Span/Generic type expects at least one inner type"); + } + + // Array and Span are processed exactly the same, using `Vec`. + let is_array = current_type.contains("core::array::Array") + || current_type.contains("core::array::Span"); + + if is_array { + if inners.len() == 1 { + Self::Array(AbiArray::new(current_type, inners[0].clone())) + } else { + panic!("Array/Span expect exactly one inner type"); + } + } else { + Self::Generic(AbiGeneric::new(current_type, inners)) + } + } + + /// Parses a tuple, which can also contains nested types. + fn parse_tuple(chars: &mut Peekable) -> Self { + let mut tuple_values = Vec::new(); + + if chars.next_if(|&x| x == ')').is_some() { + return Self::Basic("()".into()); + } + + while let Some(c) = chars.peek() { + match c { + ',' => { + chars.next(); + } + ')' => { + chars.next(); + break; + } + _ => { + tuple_values.push(Self::parse_type(chars)); + } + } + } + + Self::Tuple(AbiTuple::new(tuple_values)) + } +} diff --git a/starknet-contract/src/abi/parser/abi_types/tuple.rs b/starknet-contract/src/abi/parser/abi_types/tuple.rs new file mode 100644 index 00000000..ae696453 --- /dev/null +++ b/starknet-contract/src/abi/parser/abi_types/tuple.rs @@ -0,0 +1,262 @@ +use super::{AbiType, AbiTypeAny, GENTY_FROZEN}; + +#[derive(Debug, PartialEq, Clone)] +pub struct AbiTuple { + pub inners: Vec, + pub genty: String, +} + +impl AbiTuple { + pub fn new(inners: Vec) -> Self { + AbiTuple { + inners, + genty: String::new(), + } + } +} + +impl From> for AbiTuple { + fn from(v: Vec) -> Self { + Self::new(v) + } +} + +impl AbiType for AbiTuple { + fn get_genty(&self) -> String { + self.genty.clone() + } + + fn compare_generic(&mut self, other: &AbiTypeAny) { + match other { + AbiTypeAny::Tuple(_) => { + if self.genty != GENTY_FROZEN { + self.genty = other.get_genty(); + } + } + _ => { + for inner in &mut self.inners { + inner.compare_generic(other); + } + } + }; + } + + fn apply_generic(&mut self, cairo_types_gentys: Vec<(&str, &str)>) -> (String, bool) { + // Check if the whole tuple is the generic. + for (cairo_type, genty) in &cairo_types_gentys { + if &self.get_cairo_type_full() == cairo_type { + self.genty = genty.to_string(); + return (genty.to_string(), true); + } + } + + let mut tuple_has_generic = false; + let mut s = "(".to_string(); + let arr_len = self.inners.len(); + + for (idx, inner) in self.inners.iter_mut().enumerate() { + let (type_str, is_generic) = inner.apply_generic(cairo_types_gentys.clone()); + + if is_generic && !tuple_has_generic { + tuple_has_generic = true; + } + + s.push_str(&type_str); + + if idx < arr_len - 1 { + s.push_str(", "); + } + } + s.push(')'); + + (s, tuple_has_generic) + } + + fn get_cairo_type_full(&self) -> String { + let mut s = "(".to_string(); + for (idx, inner) in self.inners.iter().enumerate() { + s.push_str(&inner.get_cairo_type_full()); + + if idx < self.inners.len() - 1 { + s.push_str(", "); + } + } + s.push(')'); + s + } + + fn get_cairo_type_name(&self) -> String { + "|tuple|".to_string() + } + + fn to_rust_type(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + let mut s = "(".to_string(); + for (idx, inner) in self.inners.iter().enumerate() { + s.push_str(&inner.to_rust_type()); + + if idx < self.inners.len() - 1 { + s.push_str(", "); + } + } + s.push(')'); + s + } + } + + fn to_rust_type_path(&self) -> String { + if !self.genty.is_empty() && self.genty != GENTY_FROZEN { + self.genty.clone() + } else { + let mut s = "(".to_string(); + for (idx, inner) in self.inners.iter().enumerate() { + s.push_str(&inner.to_rust_type_path()); + + if idx < self.inners.len() - 1 { + s.push_str(", "); + } + } + s.push(')'); + s + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; + + fn get_default() -> AbiTuple { + AbiTuple::new( + vec![ + AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), + AbiTypeAny::Basic(AbiBasic::new("core::integer::u32".into())), + ] + .into(), + ) + } + + #[test] + fn get_cairo_type_full() { + let t = get_default(); + assert_eq!( + t.get_cairo_type_full(), + "(core::felt252, core::integer::u32)" + ); + } + + #[test] + fn cairo_type_name_only() { + let t = get_default(); + assert_eq!(t.get_cairo_type_name(), "|tuple|"); + } + + #[test] + fn to_rust_type() { + let t = get_default(); + assert_eq!( + t.to_rust_type(), + "(starknet::core::types::FieldElement, u32)" + ); + } + + #[test] + fn to_rust_type_path() { + let t = get_default(); + assert_eq!( + t.to_rust_type_path(), + "(starknet::core::types::FieldElement, u32)" + ); + } + + #[test] + fn from_string() { + let t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!(t, AbiTypeAny::Tuple(get_default())); + } + + #[test] + fn from_string_tuple_of_array() { + let t = + AbiTypeAny::from_string("(core::array::Array::, core::integer::u32)"); + assert_eq!( + t, + AbiTypeAny::Tuple( + vec![ + AbiTypeAny::Array(AbiArray::new( + "core::array::Array", + AbiTypeAny::Basic("core::felt252".into()) + )), + AbiTypeAny::Basic("core::integer::u32".into()), + ] + .into() + ) + ); + } + + #[test] + fn generic_tuple() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("(core::felt252, core::integer::u32)", "A")]), + ("A".to_string(), true) + ); + } + + #[test] + fn generic_inner() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A")]), + ("(A, core::integer::u32)".to_string(), true) + ); + } + + #[test] + fn generic_inner_2() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("core::integer::u32", "A")]), + ("(core::felt252, A)".to_string(), true) + ); + } + + #[test] + fn generic_tuple_not() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("(core::u32, core::u256)", "A")]), + ("(core::felt252, core::integer::u32)".to_string(), false) + ); + } + + #[test] + fn generic_inner_not() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("core::u256", "A")]), + ("(core::felt252, core::integer::u32)".to_string(), false) + ); + } + + #[test] + fn generic_inner_multiple() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("core::felt252", "A"), ("core::integer::u32", "B")]), + ("(A, B)".to_string(), true) + ); + } + + #[test] + fn generic_inner_multiple_2() { + let mut t = AbiTypeAny::from_string("(core::felt252, core::integer::u32)"); + assert_eq!( + t.apply_generic(vec![("core::array", "A"), ("core::integer::u32", "B")]), + ("(core::felt252, B)".to_string(), true) + ); + } +} diff --git a/starknet-contract/src/abi/parser/cairo_enum.rs b/starknet-contract/src/abi/parser/cairo_enum.rs new file mode 100644 index 00000000..1c8ae48c --- /dev/null +++ b/starknet-contract/src/abi/parser/cairo_enum.rs @@ -0,0 +1,81 @@ +use std::collections::HashMap; + +use starknet_core::types::contract::AbiNamedMember; + +use super::abi_types::{AbiType, AbiTypeAny}; + +#[derive(Debug, Clone)] +pub struct CairoEnum { + pub abi: AbiTypeAny, + /// Parsed types for each variants. + pub variants: Vec<(String, AbiTypeAny)>, + /// Variant name => (generic representation, is_generic). + pub generic_variants: HashMap, +} + +impl CairoEnum { + /// Gets the name of the enum type. + pub fn get_name(&self) -> String { + self.abi.get_cairo_type_name() + } + + /// Returns true if the enum is generic, false otherwise. + pub fn is_generic(&self) -> bool { + matches!(self.abi, AbiTypeAny::Generic(_)) + } + + /// Returns the list of generic types, if any. + pub fn get_gentys(&self) -> Vec { + if let AbiTypeAny::Generic(g) = &self.abi { + g.get_gentys_only() + } else { + vec![] + } + } + + /// Initializes a new instance from the abi name and it's variants. + pub fn new(abi_name: &str, abi_variants: &Vec) -> CairoEnum { + let abi = AbiTypeAny::from_string(abi_name); + let mut variants: Vec<(String, AbiTypeAny)> = vec![]; + let mut generic_variants: HashMap = HashMap::new(); + + for v in abi_variants { + let name = v.name.clone(); + let mut v_abi = AbiTypeAny::from_string(&v.r#type.clone()); + + if let AbiTypeAny::Generic(ref g) = abi { + let cairo_gentys = g.get_cairo_types_gentys(); + let cairo_gentys = cairo_gentys + .iter() + .map(|(v1, v2)| (&v1[..], &v2[..])) + .collect(); + + let (type_str, is_generic) = v_abi.apply_generic(cairo_gentys); + + generic_variants.insert(name.clone(), (type_str.clone(), is_generic)); + } + + variants.push((name.clone(), v_abi.clone())); + } + + CairoEnum { + abi, + variants, + generic_variants, + } + } + + /// Compares the generic types for each variants with an other `CairoEnum`. + pub fn compare_generic_types(&self, existing_ce: &mut CairoEnum) { + if let AbiTypeAny::Generic(_) = &self.abi { + for (ev_name, ev_abi) in &mut existing_ce.variants { + for (v_name, v_abi) in &self.variants { + if v_name != ev_name { + continue; + } + ev_abi.compare_generic(v_abi); + } + } + } + } +} diff --git a/starknet-contract/src/abi/parser/cairo_event.rs b/starknet-contract/src/abi/parser/cairo_event.rs new file mode 100644 index 00000000..46e7296b --- /dev/null +++ b/starknet-contract/src/abi/parser/cairo_event.rs @@ -0,0 +1,108 @@ +//! Event parsing. +use starknet_core::types::contract::{AbiEvent, AbiNamedMember, EventFieldKind, TypedAbiEvent}; + +use super::abi_types::{AbiType, AbiTypeAny}; +use super::{CairoEnum, CairoStruct}; + +#[derive(Debug, Clone)] +pub enum CairoEventInner { + Enum(CairoEnum), + Struct(CairoStruct), +} + +#[derive(Debug, Clone)] +pub struct CairoEvent { + pub abi: AbiTypeAny, + pub inner: CairoEventInner, + pub fields_kinds: Vec, +} + +impl CairoEvent { + /// Gets the name of the struct type. + pub fn get_name(&self) -> String { + self.abi.get_cairo_type_name() + } + + /// Gets the count for each field kind (keys, data). + pub fn count_fields_kinds(&self) -> (usize, usize) { + let mut k = 0; + let mut d = 0; + + for fk in &self.fields_kinds { + match fk { + EventFieldKind::Key => k += 1, + EventFieldKind::Data => d += 1, + _ => continue, + } + } + + (k, d) + } + + /// Initializes a new instance from the abi name and it's members. + pub fn new(abi_event: &AbiEvent) -> Option { + match abi_event { + AbiEvent::Typed(typed_e) => match typed_e { + TypedAbiEvent::Struct(s) => { + if s.members.is_empty() { + return None; + } + + let name = &s.name; + let mut kinds = vec![]; + let members = s + .members + .iter() + .map(|m| { + kinds.push(m.kind.clone()); + AbiNamedMember { + name: m.name.clone(), + r#type: m.r#type.clone(), + } + }) + .collect(); + + let cs = CairoStruct::new(name, &members); + + Some(CairoEvent { + abi: AbiTypeAny::from_string(name), + inner: CairoEventInner::Struct(cs), + fields_kinds: kinds, + }) + } + TypedAbiEvent::Enum(e) => { + if e.variants.is_empty() { + return None; + } + + let name = &e.name; + let mut kinds = vec![]; + let variants = e + .variants + .iter() + .map(|v| { + kinds.push(v.kind.clone()); + AbiNamedMember { + name: v.name.clone(), + r#type: v.r#type.clone(), + } + }) + .collect(); + + let ce = CairoEnum::new(name, &variants); + + Some(CairoEvent { + abi: AbiTypeAny::from_string(name), + inner: CairoEventInner::Enum(ce), + fields_kinds: kinds, + }) + } + }, + AbiEvent::Untyped(_) => { + // Can we support this..? + //panic!("Untyped events are not supported"); + None + } + } + } +} diff --git a/starknet-contract/src/abi/parser/cairo_function.rs b/starknet-contract/src/abi/parser/cairo_function.rs new file mode 100644 index 00000000..562544ab --- /dev/null +++ b/starknet-contract/src/abi/parser/cairo_function.rs @@ -0,0 +1,46 @@ +use starknet_core::types::contract::{AbiNamedMember, AbiOutput, StateMutability}; + +use super::abi_types::AbiTypeAny; + +#[derive(Debug, Clone)] +pub struct CairoFunction { + pub name: String, + pub state_mutability: StateMutability, + pub inputs: Vec<(String, AbiTypeAny)>, + // For now, only one output type is supported (or none). + // TODO: investigate the cases where more than one output is + // present in the ABI. + pub output: Option, +} + +impl CairoFunction { + /// Initializes a new instance from the abi name and it's members. + pub fn new( + abi_name: &str, + state_mutability: StateMutability, + inputs: &[AbiNamedMember], + outputs: &Vec, + ) -> CairoFunction { + let name = abi_name.to_string(); + + let output = if !outputs.is_empty() { + // For now, only first output is considered. + // TODO: investigate when we can have several outputs. + Some(AbiTypeAny::from_string(&outputs[0].r#type)) + } else { + None + }; + + let inputs = inputs + .iter() + .map(|i| (i.name.clone(), AbiTypeAny::from_string(&i.r#type))) + .collect(); + + CairoFunction { + name, + state_mutability, + inputs, + output, + } + } +} diff --git a/starknet-contract/src/abi/parser/cairo_struct.rs b/starknet-contract/src/abi/parser/cairo_struct.rs new file mode 100644 index 00000000..25d04bc6 --- /dev/null +++ b/starknet-contract/src/abi/parser/cairo_struct.rs @@ -0,0 +1,80 @@ +use std::collections::HashMap; +use starknet_core::types::contract::AbiNamedMember; + +use super::abi_types::{AbiType, AbiTypeAny}; + +#[derive(Debug, Clone)] +pub struct CairoStruct { + pub abi: AbiTypeAny, + /// Parsed types for each member. + pub members: Vec<(String, AbiTypeAny)>, + /// Members name => (generic representation, is_generic). + pub generic_members: HashMap, +} + +impl CairoStruct { + /// Gets the name of the struct type. + pub fn get_name(&self) -> String { + self.abi.get_cairo_type_name() + } + + /// Returns true if the struct is generic, false otherwise. + pub fn is_generic(&self) -> bool { + matches!(self.abi, AbiTypeAny::Generic(_)) + } + + /// Returns the list of generic types, if any. + pub fn get_gentys(&self) -> Vec { + if let AbiTypeAny::Generic(g) = &self.abi { + g.get_gentys_only() + } else { + vec![] + } + } + + /// Initializes a new instance from the abi name and it's members. + pub fn new(abi_name: &str, abi_members: &Vec) -> CairoStruct { + let abi = AbiTypeAny::from_string(abi_name); + let mut members: Vec<(String, AbiTypeAny)> = vec![]; + let mut generic_members: HashMap = HashMap::new(); + + for m in abi_members { + let name = m.name.clone(); + let mut m_abi = AbiTypeAny::from_string(&m.r#type.clone()); + + if let AbiTypeAny::Generic(ref g) = abi { + let cairo_gentys = g.get_cairo_types_gentys(); + let cairo_gentys = cairo_gentys + .iter() + .map(|(v1, v2)| (&v1[..], &v2[..])) + .collect(); + + let (type_str, is_generic) = m_abi.apply_generic(cairo_gentys); + + generic_members.insert(name.clone(), (type_str.clone(), is_generic)); + } + + members.push((name.clone(), m_abi.clone())); + } + + CairoStruct { + abi, + members, + generic_members, + } + } + + /// Compares the generic types for each members with an other `CairoStruct`. + pub fn compare_generic_types(&self, existing_cs: &mut CairoStruct) { + if let AbiTypeAny::Generic(_) = &self.abi { + for (em_name, em_abi) in &mut existing_cs.members { + for (m_name, m_abi) in &self.members { + if m_name != em_name { + continue; + } + em_abi.compare_generic(m_abi); + } + } + } + } +} diff --git a/starknet-contract/src/abi/parser/mod.rs b/starknet-contract/src/abi/parser/mod.rs new file mode 100644 index 00000000..c2934bac --- /dev/null +++ b/starknet-contract/src/abi/parser/mod.rs @@ -0,0 +1,27 @@ +//! This crates is about parsing Cairo types from an ABI. +//! Later, this will also be able to parse Cairo type from Cairo code. +//! +//! The important consideration are the generic type. Indeed, in the ABI +//! there is no information about a type genericity and how exactly +//! the members/variants are following the generic type as everything is +//! flattened. +//! +//! `abi_types` is the low level parsing of the types. It supports +//! nested types. +//! +//! `CairoStruct`, `CairoEnum` and `CairoFunction` are higher level +//! types to resolve the genericity and manage members/variants/inputs/outputs +//! for simpler expansion. +mod abi_types; + +mod cairo_struct; +pub use cairo_struct::CairoStruct; + +mod cairo_enum; +pub use cairo_enum::CairoEnum; + +mod cairo_function; +pub use cairo_function::CairoFunction; + +mod cairo_event; +pub use cairo_event::CairoEvent; diff --git a/starknet-contract/src/lib.rs b/starknet-contract/src/lib.rs index cec06190..0ef224e7 100644 --- a/starknet-contract/src/lib.rs +++ b/starknet-contract/src/lib.rs @@ -1,2 +1,5 @@ mod factory; pub use factory::ContractFactory; + +mod abi; + From 5c994fac7c2ad8168f6df99eae80e1a7d850711e Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 23 Sep 2023 21:50:13 -0600 Subject: [PATCH 02/31] add abigen --- Cargo.lock | 4 + starknet-contract/src/abi/mod.rs | 4 +- starknet-contract/src/abi/parser/mod.rs | 4 +- starknet-contract/src/lib.rs | 3 +- starknet-macros/Cargo.toml | 4 + starknet-macros/src/abigen/contract_abi.rs | 56 +++++ starknet-macros/src/abigen/expand/contract.rs | 46 ++++ starknet-macros/src/abigen/expand/enum.rs | 150 ++++++++++++ starknet-macros/src/abigen/expand/event.rs | 198 ++++++++++++++++ starknet-macros/src/abigen/expand/function.rs | 218 ++++++++++++++++++ starknet-macros/src/abigen/expand/generic.rs | 38 +++ starknet-macros/src/abigen/expand/mod.rs | 20 ++ starknet-macros/src/abigen/expand/struct.rs | 128 ++++++++++ starknet-macros/src/abigen/expand/utils.rs | 17 ++ starknet-macros/src/abigen/mod.rs | 118 ++++++++++ starknet-macros/src/lib.rs | 8 + 16 files changed, 1010 insertions(+), 6 deletions(-) create mode 100644 starknet-macros/src/abigen/contract_abi.rs create mode 100644 starknet-macros/src/abigen/expand/contract.rs create mode 100644 starknet-macros/src/abigen/expand/enum.rs create mode 100644 starknet-macros/src/abigen/expand/event.rs create mode 100644 starknet-macros/src/abigen/expand/function.rs create mode 100644 starknet-macros/src/abigen/expand/generic.rs create mode 100644 starknet-macros/src/abigen/expand/mod.rs create mode 100644 starknet-macros/src/abigen/expand/struct.rs create mode 100644 starknet-macros/src/abigen/expand/utils.rs create mode 100644 starknet-macros/src/abigen/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 02187b44..3c85e1d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1771,6 +1771,10 @@ dependencies = [ name = "starknet-macros" version = "0.1.3" dependencies = [ + "proc-macro2", + "quote", + "serde_json", + "starknet-contract", "starknet-core", "syn 2.0.15", ] diff --git a/starknet-contract/src/abi/mod.rs b/starknet-contract/src/abi/mod.rs index 1b631d25..1ee0a719 100644 --- a/starknet-contract/src/abi/mod.rs +++ b/starknet-contract/src/abi/mod.rs @@ -1,2 +1,2 @@ -mod cairo_types; -mod parser; +pub mod cairo_types; +pub mod parser; diff --git a/starknet-contract/src/abi/parser/mod.rs b/starknet-contract/src/abi/parser/mod.rs index c2934bac..018b039d 100644 --- a/starknet-contract/src/abi/parser/mod.rs +++ b/starknet-contract/src/abi/parser/mod.rs @@ -12,7 +12,7 @@ //! `CairoStruct`, `CairoEnum` and `CairoFunction` are higher level //! types to resolve the genericity and manage members/variants/inputs/outputs //! for simpler expansion. -mod abi_types; +pub mod abi_types; mod cairo_struct; pub use cairo_struct::CairoStruct; @@ -24,4 +24,4 @@ mod cairo_function; pub use cairo_function::CairoFunction; mod cairo_event; -pub use cairo_event::CairoEvent; +pub use cairo_event::{CairoEvent, CairoEventInner}; diff --git a/starknet-contract/src/lib.rs b/starknet-contract/src/lib.rs index 0ef224e7..0e990df4 100644 --- a/starknet-contract/src/lib.rs +++ b/starknet-contract/src/lib.rs @@ -1,5 +1,4 @@ mod factory; pub use factory::ContractFactory; -mod abi; - +pub mod abi; diff --git a/starknet-macros/Cargo.toml b/starknet-macros/Cargo.toml index 473264d1..8705f4d9 100644 --- a/starknet-macros/Cargo.toml +++ b/starknet-macros/Cargo.toml @@ -17,7 +17,11 @@ proc-macro = true [dependencies] starknet-core = { version = "0.6.0", path = "../starknet-core" } +starknet-contract = { version = "0.5.0", path = "../starknet-contract" } +proc-macro2 = "1.0" +quote = "1.0" syn = "2.0.15" +serde_json = "1.0.74" [features] default = [] diff --git a/starknet-macros/src/abigen/contract_abi.rs b/starknet-macros/src/abigen/contract_abi.rs new file mode 100644 index 00000000..5bfa037b --- /dev/null +++ b/starknet-macros/src/abigen/contract_abi.rs @@ -0,0 +1,56 @@ +//! Defines the arguments of the `abigen` macro. +//! +//! `ContractAbi` is expected to the argument +//! passed to the macro. We should then parse the +//! token stream to ensure the arguments are correct. +//! +//! At this moment, the macro supports two fashions: +//! +//! Loading from a file. +//! +//! abigen!(ContractName, "path/to/abi.json" +//! +//! +//! Loading from a literal string ABI. +//! +//! abigen!(ContractName, r#" +//! [{ .... }] +//! "#); +//! +use std::fs::File; +use syn::{ + parse::{Parse, ParseStream, Result}, + Ident, LitStr, Token, +}; +use starknet_core::types::contract::AbiEntry; + +#[derive(Clone, Debug)] +pub(crate) struct ContractAbi { + pub name: Ident, + pub abi: Vec, +} + +impl Parse for ContractAbi { + fn parse(input: ParseStream) -> Result { + let name = input.parse::()?; + input.parse::()?; + + // Path rooted to the Cargo.toml location. + let json_path = input.parse::()?; + + let abi = serde_json::from_reader::<_, Vec>( + File::open(&json_path.value()) + .map_err(|e| { + syn::Error::new(json_path.span(), format!("JSON open file error: {}", e)) + })? + ) + .map_err(|e| { + syn::Error::new(json_path.span(), format!("JSON parse error: {}", e)) + })?; + + Ok(ContractAbi { + name, + abi, + }) + } +} diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs new file mode 100644 index 00000000..59c88619 --- /dev/null +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -0,0 +1,46 @@ +//! Expands the contract first implementation with +//! default configuration for provider and account, if any. +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use syn::Ident; + +pub struct CairoContract; + +impl CairoContract { + pub fn expand(contract_name: Ident) -> TokenStream2 { + quote! { + #[derive(Debug)] + pub struct #contract_name

+ where + P: starknet::providers::Provider + Send + Sync,

::Error: 'static + { + pub address: starknet::core::types::FieldElement, + pub provider: std::sync::Arc

, + pub account: std::option::Option, starknet::signers::LocalWallet>>>, + } + + impl

#contract_name

+ where + P: starknet::providers::Provider + Send + Sync,

::Error: 'static + { + pub fn new( + address: starknet::core::types::FieldElement, + provider: std::sync::Arc

, + ) -> Self { + Self { + address, + provider: std::sync::Arc::clone(&provider), + account: None, + } + } + + pub fn with_account(mut self, account: std::sync::Arc, starknet::signers::LocalWallet>>, + ) -> Self { + self.account = Some(std::sync::Arc::clone(&account)); + self + } + } + } + } +} diff --git a/starknet-macros/src/abigen/expand/enum.rs b/starknet-macros/src/abigen/expand/enum.rs new file mode 100644 index 00000000..431ce8a1 --- /dev/null +++ b/starknet-macros/src/abigen/expand/enum.rs @@ -0,0 +1,150 @@ +//! Enums expansion, taking in account generic types if any. +use super::{ + generic, Expandable, + utils::{str_to_ident, str_to_type} +}; + +use starknet_contract::abi::parser::{ + CairoEnum, + abi_types::{AbiType, AbiTypeAny}, +}; + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::Ident; + +impl Expandable for CairoEnum { + fn expand_decl(&self) -> TokenStream2 { + let enum_name = str_to_ident(&self.get_name()); + + let mut variants: Vec = vec![]; + + for (name, abi_type) in &self.variants { + let name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type()); + if abi_type.is_unit() { + variants.push(quote!(#name)); + } else { + variants.push(quote!(#name(#ty))); + } + } + + if self.is_generic() { + let gentys: Vec = self.get_gentys().iter().map(|g| str_to_ident(g)).collect(); + + quote! { + #[derive(Debug, PartialEq)] + pub enum #enum_name<#(#gentys),*> { + #(#variants),* + } + } + } else { + quote! { + #[derive(Debug, PartialEq)] + pub enum #enum_name { + #(#variants),* + } + } + } + } + + fn expand_impl(&self) -> TokenStream2 { + let name_str = &self.get_name(); + let enum_name = str_to_ident(name_str); + + let mut serialized_sizes: Vec = vec![]; + let mut serializations: Vec = vec![]; + let mut deserializations: Vec = vec![]; + + for (i, (name, abi_type)) in self.variants.iter().enumerate() { + let variant_name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type_path()); + + // Tuples type used as rust type item path must be surrounded + // by angle brackets. + let ty_punctuated = match abi_type { + AbiTypeAny::Tuple(_) => quote!(<#ty>), + _ => quote!(#ty), + }; + + if abi_type.is_unit() { + serializations.push(quote! { + #enum_name::#variant_name => usize::serialize(&#i) + }); + deserializations.push(quote! { + #i => Ok(#enum_name::#variant_name) + }); + serialized_sizes.push(quote! { + #enum_name::#variant_name => 1 + }); + } else { + serializations.push(quote! { + #enum_name::#variant_name(val) => { + let mut temp = vec![]; + temp.extend(usize::serialize(&#i)); + temp.extend(#ty_punctuated::serialize(val)); + temp + } + }); + deserializations.push(quote! { + #i => Ok(#enum_name::#variant_name(#ty_punctuated::deserialize(felts, offset + 1)?)) + }); + // +1 because we have to handle the variant index also. + serialized_sizes.push(quote! { + #enum_name::#variant_name(val) => #ty_punctuated::serialized_size(val) + 1 + }) + } + } + + deserializations.push(quote! { + _ => panic!("Index not handle for enum {}", #name_str) + }); + + let gentys: Vec = self.get_gentys().iter().map(|g| str_to_ident(g)).collect(); + + let impl_line = if self.is_generic() { + generic::impl_with_gentys_tokens(&enum_name, &gentys) + } else { + quote!(impl cairo_types::CairoType for #enum_name) + }; + + let rust_type = if self.is_generic() { + generic::rust_associated_type_gentys_tokens(&enum_name, &gentys) + } else { + quote!( + type RustType = Self; + ) + }; + + quote! { + #impl_line { + + #rust_type + + const SERIALIZED_SIZE: std::option::Option = std::option::Option::None; + + #[inline] + fn serialized_size(rust: &Self::RustType) -> usize { + match rust { + #(#serialized_sizes),* + } + } + + fn serialize(rust: &Self::RustType) -> Vec { + + match rust { + #(#serializations),* + } + } + + fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> cairo_types::Result { + let index:u128 = felts[offset].try_into().unwrap(); + match index as usize { + #(#deserializations),* + } + + } + } + } + } +} diff --git a/starknet-macros/src/abigen/expand/event.rs b/starknet-macros/src/abigen/expand/event.rs new file mode 100644 index 00000000..e4a711cc --- /dev/null +++ b/starknet-macros/src/abigen/expand/event.rs @@ -0,0 +1,198 @@ +//! Events expansion. +use super::{ + Expandable, ExpandableEvent, + utils::{str_to_ident, str_to_litstr, str_to_type} +}; + +use starknet_contract::abi::parser::{ + CairoEvent, CairoEventInner, + abi_types::{AbiType, AbiTypeAny} +}; +use starknet_core::types::contract::EventFieldKind; + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +impl ExpandableEvent for CairoEvent { + fn expand_decl(&self) -> TokenStream2 { + let decl = match &self.inner { + CairoEventInner::Struct(s) => s.expand_decl(), + CairoEventInner::Enum(e) => e.expand_decl(), + }; + quote!(#decl) + } + + fn expand_impl(&self, events: &[CairoEvent]) -> TokenStream2 { + let mut tokens = vec![]; + + let inner_imp = match &self.inner { + CairoEventInner::Struct(s) => s.expand_impl(), + CairoEventInner::Enum(e) => e.expand_impl(), + }; + + tokens.push(quote!(#inner_imp)); + + // Generate the get_selector() method for this event. + let name_ident = str_to_ident(&self.get_name()); + let name_str = str_to_litstr(&self.get_name()); + let selector = quote! { + impl #name_ident { + pub fn get_selector() -> starknet::core::types::FieldElement { + starknet::macros::selector!(#name_str) + } + } + }; + + tokens.push(selector); + + // Stop here if it's not the Event enum. + if self.get_name() != "Event" { + return quote! { + #(#tokens)* + }; + } + + // If it's the Event enum, we can generate the TryFrom. + + // It should always be an enum here. + if let CairoEventInner::Enum(inner) = &self.inner { + let mut variants_tokens = vec![]; + + for (v_name, _) in &inner.variants { + // Get the corresponding CairoEvent in the array to access it's fields. + let cev = events + .iter() + .find(|&e| &e.get_name() == v_name) + .unwrap_or_else(|| panic!("Event variant {} was not found in events", v_name)); + + let _cev_fields_kinds = cev.count_fields_kinds(); + + let mut desers_tokens = vec![]; + let mut names_tokens = vec![]; + let v_ident = str_to_ident(v_name); + let v_name_str = str_to_litstr(v_name); + + // Let's write the deserialization of each member/variants + // of the current event. + match &cev.inner { + CairoEventInner::Struct(s) => { + for (idx, (name, abi_type)) in s.members.iter().enumerate() { + let kind = &cev.fields_kinds[idx]; + let name_str = str_to_litstr(name); + let name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type_path()); + let ty_punctuated = match abi_type { + AbiTypeAny::Tuple(_) => quote!(<#ty>), + _ => quote!(#ty), + }; + + match kind { + EventFieldKind::Key => { + desers_tokens.push(quote! { + let #name = match #ty_punctuated::deserialize(&event.keys, key_offset) { + Ok(v) => v, + Err(e) => return Err(format!("Could not deserialize field {} for {}: {:?}", #name_str, #v_name_str, e)), + }; + key_offset += #ty_punctuated::serialized_size(&#name); + }); + } + EventFieldKind::Data => { + desers_tokens.push(quote! { + let #name = match #ty_punctuated::deserialize(&event.data, data_offset) { + Ok(v) => v, + Err(e) => return Err(format!("Could not deserialize field {} for {}: {:?}", #name_str, #v_name_str, e)), + }; + data_offset += #ty_punctuated::serialized_size(&#name); + }); + } + _ => {} + }; + + names_tokens.push(quote!(#name)); + } + } + CairoEventInner::Enum(e) => { + for (idx, (name, abi_type)) in e.variants.iter().enumerate() { + let kind = &cev.fields_kinds[idx]; + let name_str = str_to_litstr(name); + let name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type_path()); + let ty_punctuated = match abi_type { + AbiTypeAny::Tuple(_) => quote!(<#ty>), + _ => quote!(#ty), + }; + + match kind { + EventFieldKind::Key => { + desers_tokens.push(quote! { + let #name = match #ty_punctuated::deserialize(&event.keys, key_offset) { + Ok(v) => v, + Err(e) => return Err(format!("Could not deserialize field {} for {}: {:?}", #name_str, #v_name_str, e)), + }; + key_offset += #ty_punctuated::serialized_size(&#name); + }); + } + EventFieldKind::Data => { + desers_tokens.push(quote! { + let #name = match #ty_punctuated::deserialize(&event.data, data_offset) { + Ok(v) => v, + Err(e) => return Err(format!("Could not deserialize field {} for {}: {:?}", #name_str, #v_name_str, e)), + }; + data_offset += #ty_punctuated::serialized_size(&#name); + }); + } + _ => {} + }; + + names_tokens.push(quote!(#name)); + } + } + }; + + let variant = quote! { + if selector == #v_ident::get_selector() { + // TODO: add a validation to check keys len and data len. + // To have a nice error message if the event is not formatted as + // expected. + + // We skip the selector. + let mut key_offset = 1; + let mut data_offset = 0; + + #(#desers_tokens)* + + return Ok(Event::#v_ident(#v_ident { + #(#names_tokens),* + })) + }; + }; + + variants_tokens.push(variant); + } + + // TODO: change for custom type instead of str for error? + let try_from = quote! { + impl TryFrom for Event { + type Error = String; + + fn try_from(event: starknet::core::types::EmittedEvent) -> Result { + if event.keys.is_empty() { + return Err("Missing event selector, no keys found".to_string()); + } + let selector = event.keys[0]; + + #(#variants_tokens)* + + Err(format!("Could not match any event from selector {:#064x}", selector)) + } + } + }; + + tokens.push(try_from); + } + + quote! { + #(#tokens)* + } + } +} diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs new file mode 100644 index 00000000..4b51a0ad --- /dev/null +++ b/starknet-macros/src/abigen/expand/function.rs @@ -0,0 +1,218 @@ +use super::{ + Expandable, + utils::{str_to_ident, str_to_type}, +}; +use starknet_contract::abi::parser::{ + CairoFunction, + abi_types::{AbiType, AbiTypeAny} +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use starknet_core::types::contract::StateMutability; + +impl Expandable for CairoFunction { + fn expand_decl(&self) -> TokenStream2 { + let func_name = str_to_ident(&self.name); + + let mut inputs: Vec = vec![]; + for (name, abi_type) in &self.inputs { + let name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type()); + // We can pass a reference here as serialize always takes a reference. + inputs.push(quote!(#name:&#ty)); + } + + let output = match self.state_mutability { + StateMutability::View => match &self.output { + Some(o) => { + let oty = str_to_type(&o.to_rust_type()); + quote!(-> cairo_types::Result<#oty>) + } + None => quote!(), + }, + StateMutability::External => { + // Only the TX hash is returned on success. + // TODO: go away from anyhow? Should it + // be something like cairo_contracts::Result? + quote!(-> anyhow::Result) + } + }; + + quote! { + pub async fn #func_name( + &self, + #(#inputs),* + ) #output + } + } + + fn expand_impl(&self) -> TokenStream2 { + let decl = self.expand_decl(); + let func_name = &self.name; + + let mut serializations: Vec = vec![]; + for (name, abi_type) in &self.inputs { + let name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type_path()); + + let ser = match abi_type { + AbiTypeAny::Tuple(_) => quote! { + calldata.extend(<#ty>::serialize(#name)); + }, + _ => quote!(calldata.extend(#ty::serialize(#name));), + }; + serializations.push(ser); + } + + let out_res = match &self.output { + Some(o) => { + let out_type_path = str_to_type(&o.to_rust_type_path()); + match o { + // Tuples type used as rust type path must be surrounded + // by LT/GT. + AbiTypeAny::Tuple(_) => quote!(<#out_type_path>::deserialize(&r, 0)), + _ => quote!(#out_type_path::deserialize(&r, 0)), + } + } + None => quote!(), + }; + + match &self.state_mutability { + StateMutability::View => quote! { + #decl { + let mut calldata = vec![]; + #(#serializations)* + + let r = self.provider + .call( + starknet::core::types::FunctionCall { + contract_address: self.address, + entry_point_selector: starknet::macros::selector!(#func_name), + calldata, + }, + BlockId::Tag(BlockTag::Pending), + ) + .await.map_err( + |err| + cairo_types::Error::Deserialize( + format!("Deserialization error {:}", err)))?; + + #out_res + } + }, + StateMutability::External => quote! { + // TODO: How can we add Fee configuration + estimate fee out of the box. + // maybe two methods are generated, one for actually running, the other + // for estimate the fees. + // Or, we can add a config struct as the last argument? Or directly + // at the initialization of the contract, we can give a config for + // fees (manual, estimated + scale factor). + // The estimate only may be done at the function level, to avoid + // altering the contract instance itself and hence races. + #decl { + // TODO: I don't know how to easily store the SingleOwnerAccount + // and it's generic types without complexifiying the whole typing. + // So it's constructed at every call. There is surely a better approach. + let account = match &self.account { + Some(a) => std::sync::Arc::clone(&a), + // TODO: better error handling here. + _ => return Err(anyhow::anyhow!("Account is required to send invoke transactions")) + }; + + let mut calldata = vec![]; + #(#serializations)* + + let calls = vec![starknet::accounts::Call { + to: self.address, + selector: starknet::macros::selector!(#func_name), + calldata, + }]; + + let execution = account.execute(calls).fee_estimate_multiplier(2f64); + // TODO: we can have manual fee here, or it can also be estimate only. + let max_fee = execution.estimate_fee().await?.overall_fee.into(); + + let invoke_tx = execution + .max_fee(max_fee) + .send().await?.transaction_hash; + + // TODO: add an option to watch and wait for the tx to have a receipt? + Ok(invoke_tx) + } + }, + } + } +} + +#[cfg(test)] +mod tests { + use crate::Expandable; + use cairo_type_parser::{ + abi_types::{AbiType, AbiTypeAny}, + CairoFunction, + }; + use proc_macro2::TokenStream2; + use quote::quote; + use starknet::core::types::contract::StateMutability; + + #[test] + fn test_decl_basic() { + let cf = CairoFunction { + name: "my_func".to_string(), + state_mutability: StateMutability::View, + inputs: vec![ + ("v1".to_string(), AbiTypeAny::Basic("core::felt252".into())), + ("v2".to_string(), AbiTypeAny::Basic("core::felt252".into())), + ], + output: Some(AbiTypeAny::Basic("core::felt252".into())), + }; + let te1 = cf.expand_decl(); + let tef1: TokenStream2 = quote!( + pub async fn my_func(&self, v1: &starknet::core::types::FieldElement, v2: &starknet::core::types::FieldElement) -> cairo_types::Result + ); + + assert_eq!(te1.to_string(), tef1.to_string()); + } + + #[test] + fn test_impl_basic() { + let cf = CairoFunction { + name: "my_func".to_string(), + state_mutability: StateMutability::View, + inputs: vec![ + ("v1".to_string(), AbiTypeAny::Basic("core::felt252".into())), + ("v2".to_string(), AbiTypeAny::Basic("core::felt252".into())), + ], + output: Some(AbiTypeAny::Basic("core::felt252".into())), + }; + let te1 = cf.expand_impl(); + + #[rustfmt::skip] + let tef1: TokenStream2 = quote!( + pub async fn my_func( + &self, + v1: &starknet::core::types::FieldElement, + v2: &starknet::core::types::FieldElement + ) -> cairo_types::Result { + let mut calldata = vec![]; + calldata.extend(starknet::core::types::FieldElement::serialize(v1)); + calldata.extend(starknet::core::types::FieldElement::serialize(v2)); + + let r = self.provider + .call( + starknet::core::types::FunctionCall { + contract_address: self.address, + entry_point_selector: starknet::macros::selector!("my_func"), + calldata, + }, + BlockId::Tag(BlockTag::Pending), + ) + .await.map_err(|err| cairo_types::Error::Deserialize(format!("Deserialization error {:}" , err)))?; + + starknet::core::types::FieldElement::deserialize(&r, 0) + } + ); + + assert_eq!(te1.to_string(), tef1.to_string()); + } +} diff --git a/starknet-macros/src/abigen/expand/generic.rs b/starknet-macros/src/abigen/expand/generic.rs new file mode 100644 index 00000000..48ce2b6f --- /dev/null +++ b/starknet-macros/src/abigen/expand/generic.rs @@ -0,0 +1,38 @@ +//! Utils functions for generic expansion. +use super::utils::str_to_ident; + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::Ident; + +/// Expands the implementation line with generic types. +pub fn impl_with_gentys_tokens(entity_name: &Ident, gentys: &Vec) -> TokenStream2 { + let gentys_rust: Vec = gentys + .iter() + .map(|g| str_to_ident(format!("R{}", g).as_str())) + .collect(); + + let mut tokens = vec![]; + + tokens.push(quote! { + impl<#(#gentys),* , #(#gentys_rust),*> cairo_types::CairoType for #entity_name<#(#gentys),*> + where + }); + + for (i, g) in gentys.iter().enumerate() { + let gr = &gentys_rust[i]; + tokens.push(quote!(#g: CairoType,)); + } + + quote!(#(#tokens)*) +} + +/// Expands the associated types lines for generic types. +pub fn rust_associated_type_gentys_tokens(entity_name: &Ident, gentys: &[Ident]) -> TokenStream2 { + let gentys_rust: Vec = gentys + .iter() + .map(|g| str_to_ident(format!("R{}", g).as_str())) + .collect(); + + quote!(type RustType = #entity_name<#(#gentys_rust),*>;) +} diff --git a/starknet-macros/src/abigen/expand/mod.rs b/starknet-macros/src/abigen/expand/mod.rs new file mode 100644 index 00000000..1aa80c77 --- /dev/null +++ b/starknet-macros/src/abigen/expand/mod.rs @@ -0,0 +1,20 @@ +pub(crate) mod contract; +pub(crate) mod r#enum; +pub(crate) mod event; +pub(crate) mod function; +pub(crate) mod generic; +pub(crate) mod r#struct; +pub(crate) mod utils; + +use starknet_contract::abi::parser::CairoEvent; +use proc_macro2::TokenStream as TokenStream2; + +pub trait Expandable { + fn expand_decl(&self) -> TokenStream2; + fn expand_impl(&self) -> TokenStream2; +} + +pub trait ExpandableEvent { + fn expand_decl(&self) -> TokenStream2; + fn expand_impl(&self, events: &[CairoEvent]) -> TokenStream2; +} diff --git a/starknet-macros/src/abigen/expand/struct.rs b/starknet-macros/src/abigen/expand/struct.rs new file mode 100644 index 00000000..c4368bc3 --- /dev/null +++ b/starknet-macros/src/abigen/expand/struct.rs @@ -0,0 +1,128 @@ +//! Struct expansion, taking in account generic types if any. +use super::{ + generic, Expandable, + utils::{str_to_ident, str_to_type} +}; + +use starknet_contract::abi::parser::{ + CairoStruct, + abi_types::{AbiType, AbiTypeAny}, +}; + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::Ident; + +impl Expandable for CairoStruct { + fn expand_decl(&self) -> TokenStream2 { + let struct_name = str_to_ident(&self.get_name()); + + let mut members: Vec = vec![]; + for (name, abi_type) in &self.members { + let name = str_to_ident(name); + let ty = str_to_type(&abi_type.to_rust_type()); + + members.push(quote!(#name: #ty)); + } + + if self.is_generic() { + let gentys: Vec = self.get_gentys().iter().map(|g| str_to_ident(g)).collect(); + + quote! { + #[derive(Debug, PartialEq)] + pub struct #struct_name<#(#gentys),*> { + #(pub #members),* + } + } + } else { + quote! { + #[derive(Debug, PartialEq)] + pub struct #struct_name { + #(pub #members),* + } + } + } + } + + fn expand_impl(&self) -> TokenStream2 { + let struct_name = str_to_ident(&self.get_name()); + + let mut sizes: Vec = vec![]; + let mut sers: Vec = vec![]; + let mut desers: Vec = vec![]; + let mut names: Vec = vec![]; + + let mut is_first = true; + for (name, abi_type) in &self.members { + let name = str_to_ident(name); + names.push(quote!(#name)); + + let ty = str_to_type(&abi_type.to_rust_type_path()); + + // Tuples type used as rust type item path must be surrounded + // by angle brackets. + let ty_punctuated = match abi_type { + AbiTypeAny::Tuple(_) => quote!(<#ty>), + _ => quote!(#ty), + }; + + if is_first { + sizes.push(quote!(#ty_punctuated::serialized_size(&rust.#name))); + is_first = false; + } else { + sizes.push(quote!(+ #ty_punctuated::serialized_size(&rust.#name))); + } + + sers.push(quote!(out.extend(#ty_punctuated::serialize(&rust.#name));)); + + desers.push(quote! { + let #name = #ty_punctuated::deserialize(felts, offset)?; + offset += #ty_punctuated::serialized_size(&#name); + }); + } + + let gentys: Vec = self.get_gentys().iter().map(|g| str_to_ident(g)).collect(); + + let impl_line = if self.is_generic() { + generic::impl_with_gentys_tokens(&struct_name, &gentys) + } else { + quote!(impl cairo_types::CairoType for #struct_name) + }; + + let rust_type = if self.is_generic() { + generic::rust_associated_type_gentys_tokens(&struct_name, &gentys) + } else { + quote!( + type RustType = Self; + ) + }; + + quote! { + #impl_line { + + #rust_type + + const SERIALIZED_SIZE: std::option::Option = None; + + #[inline] + fn serialized_size(rust: &Self::RustType) -> usize { + #(#sizes) * + } + + fn serialize(rust: &Self::RustType) -> Vec { + let mut out: Vec = vec![]; + #(#sers)* + out + } + + fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> cairo_types::Result { + let mut offset = offset; + #(#desers)* + Ok(#struct_name { + #(#names),* + }) + } + } + } + } +} diff --git a/starknet-macros/src/abigen/expand/utils.rs b/starknet-macros/src/abigen/expand/utils.rs new file mode 100644 index 00000000..12d10591 --- /dev/null +++ b/starknet-macros/src/abigen/expand/utils.rs @@ -0,0 +1,17 @@ +//! Utils function for expansion. +use syn::{Ident, LitStr, Type}; + +/// +pub fn str_to_ident(str_in: &str) -> Ident { + Ident::new(str_in, proc_macro2::Span::call_site()) +} + +/// +pub fn str_to_type(str_in: &str) -> Type { + syn::parse_str(str_in).unwrap_or_else(|_| panic!("Can't convert {} to syn::Type", str_in)) +} + +/// +pub fn str_to_litstr(str_in: &str) -> LitStr { + LitStr::new(str_in, proc_macro2::Span::call_site()) +} diff --git a/starknet-macros/src/abigen/mod.rs b/starknet-macros/src/abigen/mod.rs new file mode 100644 index 00000000..95091a70 --- /dev/null +++ b/starknet-macros/src/abigen/mod.rs @@ -0,0 +1,118 @@ +//! This crate contains all the logic to expand the parsed ABI types into +//! rust code. +//! +//! Important note, functions can't be generic when they are entry point +//! of a Cairo contracts. +//! For this reason, all the generic types are handles for structs and enums +//! generation only, and then applied on functions inputs/output. +//! +//! As the ABI as everything flatten, we must ensure that structs and enums are +//! checked for genericty to avoid duplicated types and detect correctly +//! the members/variants that are generic. +use proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; +use proc_macro2::TokenStream as TokenStream2; + +use std::collections::HashMap; + +use starknet_contract::abi::parser::{CairoEnum, CairoEvent, CairoFunction, CairoStruct}; +use starknet_contract::abi::cairo_types::{CAIRO_BASIC_ENUMS, CAIRO_BASIC_STRUCTS}; +use starknet_core::types::contract::AbiEntry; + +mod expand; +use expand::contract::CairoContract; +use expand::{Expandable, ExpandableEvent}; + +mod contract_abi; +use contract_abi::ContractAbi; + +pub fn abigen_internal(input: TokenStream) -> TokenStream { + let contract_abi = parse_macro_input!(input as ContractAbi); + let contract_name = contract_abi.name; + let abi = contract_abi.abi; + + let mut tokens: Vec = vec![]; + + tokens.push(CairoContract::expand(contract_name.clone())); + + let mut structs: HashMap = HashMap::new(); + let mut enums: HashMap = HashMap::new(); + let mut functions = vec![]; + let mut events = vec![]; + + for entry in &abi { + match entry { + AbiEntry::Struct(s) => { + let cs = CairoStruct::new(&s.name, &s.members); + + if CAIRO_BASIC_STRUCTS.contains(&cs.get_name().as_str()) { + continue; + } + + if let Some(ref mut existing_cs) = structs.get_mut(&cs.get_name()) { + cs.compare_generic_types(existing_cs); + } else { + structs.insert(cs.get_name(), cs.clone()); + } + } + AbiEntry::Enum(e) => { + let ce = CairoEnum::new(&e.name, &e.variants); + + if CAIRO_BASIC_ENUMS.contains(&ce.get_name().as_str()) { + continue; + } + + if let Some(ref mut existing_ce) = enums.get_mut(&ce.get_name()) { + ce.compare_generic_types(existing_ce); + } else { + enums.insert(ce.get_name(), ce.clone()); + } + } + AbiEntry::Function(f) => { + // Functions cannot be generic when they are entry point. + // From this statement, we can safely assume that any function name is + // unique. + let cf = + CairoFunction::new(&f.name, f.state_mutability.clone(), &f.inputs, &f.outputs); + functions.push(cf.expand_impl()); + } + AbiEntry::Event(ev) => { + if let Some(cev) = CairoEvent::new(ev) { + events.push(cev); + } + } + _ => continue, + } + } + + for (_, cs) in structs { + tokens.push(cs.expand_decl()); + tokens.push(cs.expand_impl()); + } + + for (_, ce) in enums { + tokens.push(ce.expand_decl()); + tokens.push(ce.expand_impl()); + } + + for ev in &events { + tokens.push(ev.expand_decl()); + tokens.push(ev.expand_impl(&events)); + } + + tokens.push(quote! { + impl

#contract_name

+ where + P: starknet::providers::Provider + Send + Sync,

::Error: 'static + { + #(#functions)* + } + }); + + let expanded = quote! { + #(#tokens)* + }; + + expanded.into() +} diff --git a/starknet-macros/src/lib.rs b/starknet-macros/src/lib.rs index 1d887834..eb0d48a9 100644 --- a/starknet-macros/src/lib.rs +++ b/starknet-macros/src/lib.rs @@ -5,6 +5,9 @@ use starknet_core::{ }; use syn::{parse_macro_input, LitStr}; +mod abigen; +use abigen::abigen_internal; + #[proc_macro] pub fn selector(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as LitStr); @@ -115,6 +118,11 @@ pub fn felt_hex(input: TokenStream) -> TokenStream { .unwrap() } +#[proc_macro] +pub fn abigen(input: TokenStream) -> TokenStream { + abigen_internal(input) +} + #[cfg(feature = "use_imported_type")] fn field_element_path() -> &'static str { "FieldElement" From e80a0ee1ca4a8f892093c4b4bf5f28a74ff8f24f Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 23 Sep 2023 21:55:05 -0600 Subject: [PATCH 03/31] fix test abi parser --- starknet-contract/src/abi/cairo_types/types/boolean.rs | 2 +- starknet-contract/src/abi/cairo_types/types/felt.rs | 2 +- starknet-contract/src/abi/cairo_types/types/option.rs | 3 +-- starknet-contract/src/abi/cairo_types/types/result.rs | 2 +- starknet-contract/src/abi/parser/abi_types/array.rs | 2 +- starknet-contract/src/abi/parser/abi_types/basic.rs | 2 +- starknet-contract/src/abi/parser/abi_types/generic.rs | 2 +- starknet-contract/src/abi/parser/abi_types/tuple.rs | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/starknet-contract/src/abi/cairo_types/types/boolean.rs b/starknet-contract/src/abi/cairo_types/types/boolean.rs index f3b58286..964ec889 100644 --- a/starknet-contract/src/abi/cairo_types/types/boolean.rs +++ b/starknet-contract/src/abi/cairo_types/types/boolean.rs @@ -37,7 +37,7 @@ mod tests { #[test] fn test_deserialize_bool() { - let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; // allocating in the heap. + let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; assert_eq!(bool::deserialize(&felts, 0).unwrap(), false); assert_eq!(bool::deserialize(&felts, 1).unwrap(), true); assert_eq!(bool::deserialize(&felts, 2).unwrap(), false); diff --git a/starknet-contract/src/abi/cairo_types/types/felt.rs b/starknet-contract/src/abi/cairo_types/types/felt.rs index aca6f184..8a39002e 100644 --- a/starknet-contract/src/abi/cairo_types/types/felt.rs +++ b/starknet-contract/src/abi/cairo_types/types/felt.rs @@ -39,7 +39,7 @@ mod tests { #[test] fn test_deserialize_field_element() { - let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; // allocating in the heap. + let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; assert_eq!( FieldElement::deserialize(&felts, 0).unwrap(), FieldElement::ZERO diff --git a/starknet-contract/src/abi/cairo_types/types/option.rs b/starknet-contract/src/abi/cairo_types/types/option.rs index b060fe5e..a8583411 100644 --- a/starknet-contract/src/abi/cairo_types/types/option.rs +++ b/starknet-contract/src/abi/cairo_types/types/option.rs @@ -46,8 +46,7 @@ where #[cfg(test)] mod tests { use super::*; - use alloc::vec::Vec; - use starknet::core::types::FieldElement; + use starknet_core::types::FieldElement; #[test] fn test_option_some_serialize() { diff --git a/starknet-contract/src/abi/cairo_types/types/result.rs b/starknet-contract/src/abi/cairo_types/types/result.rs index 53bff34b..0bbbab3c 100644 --- a/starknet-contract/src/abi/cairo_types/types/result.rs +++ b/starknet-contract/src/abi/cairo_types/types/result.rs @@ -47,7 +47,7 @@ where #[cfg(test)] mod tests { use super::*; - use starknet::core::types::FieldElement; + use starknet_core::types::FieldElement; #[test] fn test_result_ok_serialize() { diff --git a/starknet-contract/src/abi/parser/abi_types/array.rs b/starknet-contract/src/abi/parser/abi_types/array.rs index 5a23e038..b3afa5af 100644 --- a/starknet-contract/src/abi/parser/abi_types/array.rs +++ b/starknet-contract/src/abi/parser/abi_types/array.rs @@ -87,7 +87,7 @@ impl AbiType for AbiArray { #[cfg(test)] mod tests { use super::*; - use crate::abi_types::{AbiBasic, AbiTypeAny}; + use crate::abi::parser::abi_types::{AbiBasic, AbiTypeAny}; fn get_default() -> AbiArray { AbiArray::new( diff --git a/starknet-contract/src/abi/parser/abi_types/basic.rs b/starknet-contract/src/abi/parser/abi_types/basic.rs index fb36cb32..004efb91 100644 --- a/starknet-contract/src/abi/parser/abi_types/basic.rs +++ b/starknet-contract/src/abi/parser/abi_types/basic.rs @@ -99,7 +99,7 @@ impl AbiType for AbiBasic { #[cfg(test)] mod tests { use super::*; - use crate::abi_types::AbiTypeAny; + use crate::abi::parser::abi_types::AbiTypeAny; fn get_default() -> AbiBasic { AbiBasic::new("core::felt252") diff --git a/starknet-contract/src/abi/parser/abi_types/generic.rs b/starknet-contract/src/abi/parser/abi_types/generic.rs index ba9e4291..3830129a 100644 --- a/starknet-contract/src/abi/parser/abi_types/generic.rs +++ b/starknet-contract/src/abi/parser/abi_types/generic.rs @@ -167,7 +167,7 @@ impl AbiType for AbiGeneric { #[cfg(test)] mod tests { use super::*; - use crate::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; + use crate::abi::parser::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; fn get_default() -> AbiGeneric { AbiGeneric::new( diff --git a/starknet-contract/src/abi/parser/abi_types/tuple.rs b/starknet-contract/src/abi/parser/abi_types/tuple.rs index ae696453..dd7ac252 100644 --- a/starknet-contract/src/abi/parser/abi_types/tuple.rs +++ b/starknet-contract/src/abi/parser/abi_types/tuple.rs @@ -127,7 +127,7 @@ impl AbiType for AbiTuple { #[cfg(test)] mod tests { use super::*; - use crate::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; + use crate::abi::parser::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; fn get_default() -> AbiTuple { AbiTuple::new( From 5c9628e97a2b8c1ce74bbbfb26cfc63198bf114d Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 23 Sep 2023 23:26:21 -0600 Subject: [PATCH 04/31] wip on examples --- examples/abigen.rs | 64 ++++++++++++++ examples/abigen_events.rs | 88 +++++++++++++++++++ examples/contracts_abis/events.json | 86 ++++++++++++++++++ examples/contracts_abis/mini_erc20.json | 72 +++++++++++++++ starknet-contract/src/abi/cairo_types/mod.rs | 4 +- .../src/abi/cairo_types/types/mod.rs | 16 ++-- starknet-contract/src/abi/mod.rs | 2 + starknet-macros/src/abigen/contract_abi.rs | 2 +- starknet-macros/src/abigen/expand/contract.rs | 6 ++ starknet-macros/src/abigen/expand/function.rs | 20 ++--- 10 files changed, 339 insertions(+), 21 deletions(-) create mode 100644 examples/abigen.rs create mode 100644 examples/abigen_events.rs create mode 100644 examples/contracts_abis/events.json create mode 100644 examples/contracts_abis/mini_erc20.json diff --git a/examples/abigen.rs b/examples/abigen.rs new file mode 100644 index 00000000..cbb31a9b --- /dev/null +++ b/examples/abigen.rs @@ -0,0 +1,64 @@ +use starknet::{ + // Note here, we import an ABI type. This applies for + // ContractAddress, ClassHash, EthAddress only. + accounts::{ExecutionEncoding, SingleOwnerAccount}, + core::{ + chain_id, + types::FieldElement, + }, + contract::abi::ContractAddress, + macros::{felt, abigen}, + providers::{Provider, SequencerGatewayProvider}, + signers::{LocalWallet, SigningKey}, +}; + +use std::sync::Arc; + +// Generate the bindings for the contract and also includes +// all the structs and enums present in the ABI with the exact +// same name. +abigen!(TokenContract, "./examples/contracts_abis/mini_erc20.json"); + +#[tokio::main] +async fn main() { + let provider = Arc::new(SequencerGatewayProvider::starknet_alpha_goerli()); + let eth_goerli_token_address = + felt!("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"); + + let token_contract = TokenContract::new(eth_goerli_token_address, Arc::clone(&provider)); + + // To call a view, there is no need to initialize an account. You can directly + // use the name of the method in the ABI to realize the call. + let balance: u256 = token_contract.balanceOf( + &ContractAddress(felt!("YOUR_ACCOUNT_ADDRESS_HEX_HERE"))) + .await + .expect("Call to get balance failed"); + + // For the inputs / outputs of the ABI functions, all the types are + // defined where the abigen! macro is expanded. Note that `u256` + // for the balance were already in the scope as it's generated from + // the ABI. + + // If you want to do some invoke for external functions, you must use an account. + let signer = LocalWallet::from(SigningKey::from_secret_scalar( + FieldElement::from_hex_be("YOUR_PRIVATE_KEY_IN_HEX_HERE").unwrap(), + )); + let address = FieldElement::from_hex_be("YOUR_ACCOUNT_CONTRACT_ADDRESS_IN_HEX_HERE").unwrap(); + let mut account = SingleOwnerAccount::new( + provider, + signer, + address, + chain_id::TESTNET, + ExecutionEncoding::Legacy, + ); + + let token_contract = token_contract.with_account(Arc::new(account)); + + token_contract.approve(&ContractAddress(felt!("SPENDER_ADDRESS_HEX")), &u256 { + low: 10000, + high: 0 + }); + + + println!("Your balance: {:?}", balance); +} diff --git a/examples/abigen_events.rs b/examples/abigen_events.rs new file mode 100644 index 00000000..b739d124 --- /dev/null +++ b/examples/abigen_events.rs @@ -0,0 +1,88 @@ +use starknet::{ + core::{ + types::{FieldElement, EventFilter}, + }, + accounts::{SingleOwnerAccount, ExecutionEncoding}, + macros::{felt, abigen}, + providers::{Provider, JsonRpcClient, jsonrpc::HttpTransport}, + signers::{LocalWallet, SigningKey}, +}; + +use url::Url; +use std::sync::Arc; + +// All the events are always grouped in one enun called `Event` +// in the ABI. +abigen!(Contract, "./examples/contracts_abis/events.json"); + +#[tokio::main] +async fn main() { + let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); + let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(rpc_url.clone()))); + + let signer = LocalWallet::from(SigningKey::from_secret_scalar( + FieldElement::from_hex_be("YOUR_PRIVATE_KEY_IN_HEX_HERE").unwrap(), + )); + let address = FieldElement::from_hex_be("YOUR_ACCOUNT_CONTRACT_ADDRESS_IN_HEX_HERE").unwrap(); + let account = SingleOwnerAccount::new( + provider, + signer, + address, + felt!("0x4b4154414e41"), // KATANA + ExecutionEncoding::Legacy, + ); + + let contract_address = felt!("CONTRACT_ADDRESS_HEX"); + + let event_contract = Contract::new(contract_address, Arc::clone(&provider)) + .with_account(Arc::new(account)); + + // Let emits some events by calling two externals. + event_contract + .emit_a(&FieldElement::ONE, &vec![felt!("0xff"), felt!("0xf1")]) + .await + .expect("Emit a invoke failed"); + + event_contract + .emit_b(&felt!("0x1234")) + .await + .expect("Emit b invoke failed"); + + // Fetch events with some filters with a chunck size of 100 without continuation + // token. + // This will not work on the gateway, you need to use JsonRPC node. + let event_page = provider.get_events( + EventFilter { + from_block: Some(BlockId::Number(0)), + to_block: Some(BlockId::Tag(BlockTag::Latest)), + address: None, + keys: None, + }, + None, + 100, + ).await.expect("Fetch events failed"); + + for e in event_page.events { + // abigen! macro generate for you the `TryFrom ev, + Err(_s) => { + // An event from other contracts, ignore. + continue; + } + }; + + // This way, the deserialization of the event + // is automatically done based on the variant + // from the event keys and data. + match my_event { + Event::MyEventA(a) => { + // do stuff with a.header and a.value. + } + Event::MyEventB(b) => { + // do stuff with b.value. + } + }; + } +} diff --git a/examples/contracts_abis/events.json b/examples/contracts_abis/events.json new file mode 100644 index 00000000..e3f11a00 --- /dev/null +++ b/examples/contracts_abis/events.json @@ -0,0 +1,86 @@ +[ + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "function", + "name": "emit_a", + "inputs": [ + { + "name": "header", + "type": "core::felt252" + }, + { + "name": "value", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "emit_b", + "inputs": [ + { + "name": "value", + "type": "core::felt252" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "event", + "name": "contracts::event::event::MyEventA", + "kind": "struct", + "members": [ + { + "name": "header", + "type": "core::felt252", + "kind": "key" + }, + { + "name": "value", + "type": "core::array::Span::", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "contracts::event::event::MyEventB", + "kind": "struct", + "members": [ + { + "name": "value", + "type": "core::felt252", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "contracts::event::event::Event", + "kind": "enum", + "variants": [ + { + "name": "MyEventA", + "type": "contracts::event::event::MyEventA", + "kind": "nested" + }, + { + "name": "MyEventB", + "type": "contracts::event::event::MyEventB", + "kind": "nested" + } + ] + } +] diff --git a/examples/contracts_abis/mini_erc20.json b/examples/contracts_abis/mini_erc20.json new file mode 100644 index 00000000..bc7cc76b --- /dev/null +++ b/examples/contracts_abis/mini_erc20.json @@ -0,0 +1,72 @@ +[ + { + "type": "struct", + "name": "core::integer::u256", + "members": [ + { + "name": "low", + "type": "core::integer::u128" + }, + { + "name": "high", + "type": "core::integer::u128" + } + ] + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "address", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "type": "enum", + "name": "core::bool", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "name": "spender", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "value", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "external" + }, + { + "type": "event", + "name": "contracts::basic::basic::Event", + "kind": "enum", + "variants": [] + } +] diff --git a/starknet-contract/src/abi/cairo_types/mod.rs b/starknet-contract/src/abi/cairo_types/mod.rs index 239ff626..b930ec4f 100644 --- a/starknet-contract/src/abi/cairo_types/mod.rs +++ b/starknet-contract/src/abi/cairo_types/mod.rs @@ -9,7 +9,9 @@ mod error; pub use error::{Error, Result}; -mod types; +pub mod types; +pub use types::*; +pub use types::starknet::*; use starknet_core::types::FieldElement; diff --git a/starknet-contract/src/abi/cairo_types/types/mod.rs b/starknet-contract/src/abi/cairo_types/types/mod.rs index e0322114..fad2842c 100644 --- a/starknet-contract/src/abi/cairo_types/types/mod.rs +++ b/starknet-contract/src/abi/cairo_types/types/mod.rs @@ -1,8 +1,8 @@ -mod array; -mod boolean; -mod felt; -mod integers; -mod option; -mod result; -mod starknet; -mod tuple; +pub mod array; +pub mod boolean; +pub mod felt; +pub mod integers; +pub mod option; +pub mod result; +pub mod starknet; +pub mod tuple; diff --git a/starknet-contract/src/abi/mod.rs b/starknet-contract/src/abi/mod.rs index 1ee0a719..7e37fc48 100644 --- a/starknet-contract/src/abi/mod.rs +++ b/starknet-contract/src/abi/mod.rs @@ -1,2 +1,4 @@ pub mod cairo_types; +pub use cairo_types::*; + pub mod parser; diff --git a/starknet-macros/src/abigen/contract_abi.rs b/starknet-macros/src/abigen/contract_abi.rs index 5bfa037b..1e165183 100644 --- a/starknet-macros/src/abigen/contract_abi.rs +++ b/starknet-macros/src/abigen/contract_abi.rs @@ -39,7 +39,7 @@ impl Parse for ContractAbi { let json_path = input.parse::()?; let abi = serde_json::from_reader::<_, Vec>( - File::open(&json_path.value()) + File::open(json_path.value()) .map_err(|e| { syn::Error::new(json_path.span(), format!("JSON open file error: {}", e)) })? diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index 59c88619..8109fab0 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -10,6 +10,12 @@ pub struct CairoContract; impl CairoContract { pub fn expand(contract_name: Ident) -> TokenStream2 { quote! { + + use starknet::contract::abi::cairo_types::{self, Error as CairoError}; + use starknet::contract::abi::CairoType; + use starknet::core::types::{BlockId, BlockTag}; + use starknet::accounts::Account; + #[derive(Debug)] pub struct #contract_name

where diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 4b51a0ad..c5c366b7 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -31,10 +31,10 @@ impl Expandable for CairoFunction { None => quote!(), }, StateMutability::External => { - // Only the TX hash is returned on success. - // TODO: go away from anyhow? Should it - // be something like cairo_contracts::Result? - quote!(-> anyhow::Result) + quote!(-> Result,

::Error> + > + ) } }; @@ -95,7 +95,7 @@ impl Expandable for CairoFunction { .await.map_err( |err| cairo_types::Error::Deserialize( - format!("Deserialization error {:}", err)))?; + format!("Deserialization error {}", err)))?; #out_res } @@ -116,7 +116,7 @@ impl Expandable for CairoFunction { let account = match &self.account { Some(a) => std::sync::Arc::clone(&a), // TODO: better error handling here. - _ => return Err(anyhow::anyhow!("Account is required to send invoke transactions")) + _ => panic!("Account is required to send invoke transactions") }; let mut calldata = vec![]; @@ -132,12 +132,10 @@ impl Expandable for CairoFunction { // TODO: we can have manual fee here, or it can also be estimate only. let max_fee = execution.estimate_fee().await?.overall_fee.into(); - let invoke_tx = execution + execution .max_fee(max_fee) - .send().await?.transaction_hash; - - // TODO: add an option to watch and wait for the tx to have a receipt? - Ok(invoke_tx) + .send() + .await } }, } From d1fefcd3ace33632247cbadc1b5aa75fb5a4e814 Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 23 Sep 2023 23:41:45 -0600 Subject: [PATCH 05/31] cargo fmt --- examples/abigen.rs | 23 +++++------ examples/abigen_events.rs | 41 ++++++++++--------- starknet-contract/src/abi/cairo_types/mod.rs | 2 +- .../src/abi/parser/cairo_struct.rs | 2 +- starknet-macros/src/abigen/contract_abi.rs | 25 ++++------- starknet-macros/src/abigen/expand/enum.rs | 7 ++-- starknet-macros/src/abigen/expand/event.rs | 4 +- starknet-macros/src/abigen/expand/function.rs | 8 ++-- starknet-macros/src/abigen/expand/mod.rs | 2 +- starknet-macros/src/abigen/expand/struct.rs | 7 ++-- starknet-macros/src/abigen/mod.rs | 4 +- 11 files changed, 60 insertions(+), 65 deletions(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index cbb31a9b..68142c9e 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -2,12 +2,9 @@ use starknet::{ // Note here, we import an ABI type. This applies for // ContractAddress, ClassHash, EthAddress only. accounts::{ExecutionEncoding, SingleOwnerAccount}, - core::{ - chain_id, - types::FieldElement, - }, contract::abi::ContractAddress, - macros::{felt, abigen}, + core::{chain_id, types::FieldElement}, + macros::{abigen, felt}, providers::{Provider, SequencerGatewayProvider}, signers::{LocalWallet, SigningKey}, }; @@ -29,8 +26,8 @@ async fn main() { // To call a view, there is no need to initialize an account. You can directly // use the name of the method in the ABI to realize the call. - let balance: u256 = token_contract.balanceOf( - &ContractAddress(felt!("YOUR_ACCOUNT_ADDRESS_HEX_HERE"))) + let balance: u256 = token_contract + .balanceOf(&ContractAddress(felt!("YOUR_ACCOUNT_ADDRESS_HEX_HERE"))) .await .expect("Call to get balance failed"); @@ -54,11 +51,13 @@ async fn main() { let token_contract = token_contract.with_account(Arc::new(account)); - token_contract.approve(&ContractAddress(felt!("SPENDER_ADDRESS_HEX")), &u256 { - low: 10000, - high: 0 - }); - + token_contract.approve( + &ContractAddress(felt!("SPENDER_ADDRESS_HEX")), + &u256 { + low: 10000, + high: 0, + }, + ); println!("Your balance: {:?}", balance); } diff --git a/examples/abigen_events.rs b/examples/abigen_events.rs index b739d124..248d77c5 100644 --- a/examples/abigen_events.rs +++ b/examples/abigen_events.rs @@ -1,15 +1,13 @@ use starknet::{ - core::{ - types::{FieldElement, EventFilter}, - }, - accounts::{SingleOwnerAccount, ExecutionEncoding}, - macros::{felt, abigen}, - providers::{Provider, JsonRpcClient, jsonrpc::HttpTransport}, + accounts::{ExecutionEncoding, SingleOwnerAccount}, + core::types::{EventFilter, FieldElement}, + macros::{abigen, felt}, + providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider}, signers::{LocalWallet, SigningKey}, }; -use url::Url; use std::sync::Arc; +use url::Url; // All the events are always grouped in one enun called `Event` // in the ABI. @@ -34,15 +32,15 @@ async fn main() { let contract_address = felt!("CONTRACT_ADDRESS_HEX"); - let event_contract = Contract::new(contract_address, Arc::clone(&provider)) - .with_account(Arc::new(account)); + let event_contract = + Contract::new(contract_address, Arc::clone(&provider)).with_account(Arc::new(account)); // Let emits some events by calling two externals. event_contract .emit_a(&FieldElement::ONE, &vec![felt!("0xff"), felt!("0xf1")]) .await .expect("Emit a invoke failed"); - + event_contract .emit_b(&felt!("0x1234")) .await @@ -51,16 +49,19 @@ async fn main() { // Fetch events with some filters with a chunck size of 100 without continuation // token. // This will not work on the gateway, you need to use JsonRPC node. - let event_page = provider.get_events( - EventFilter { - from_block: Some(BlockId::Number(0)), - to_block: Some(BlockId::Tag(BlockTag::Latest)), - address: None, - keys: None, - }, - None, - 100, - ).await.expect("Fetch events failed"); + let event_page = provider + .get_events( + EventFilter { + from_block: Some(BlockId::Number(0)), + to_block: Some(BlockId::Tag(BlockTag::Latest)), + address: None, + keys: None, + }, + None, + 100, + ) + .await + .expect("Fetch events failed"); for e in event_page.events { // abigen! macro generate for you the `TryFrom()?; - - let abi = serde_json::from_reader::<_, Vec>( - File::open(json_path.value()) - .map_err(|e| { - syn::Error::new(json_path.span(), format!("JSON open file error: {}", e)) - })? - ) - .map_err(|e| { - syn::Error::new(json_path.span(), format!("JSON parse error: {}", e)) - })?; - - Ok(ContractAbi { - name, - abi, - }) + + let abi = + serde_json::from_reader::<_, Vec>(File::open(json_path.value()).map_err( + |e| syn::Error::new(json_path.span(), format!("JSON open file error: {}", e)), + )?) + .map_err(|e| syn::Error::new(json_path.span(), format!("JSON parse error: {}", e)))?; + + Ok(ContractAbi { name, abi }) } } diff --git a/starknet-macros/src/abigen/expand/enum.rs b/starknet-macros/src/abigen/expand/enum.rs index 431ce8a1..2b78cb9e 100644 --- a/starknet-macros/src/abigen/expand/enum.rs +++ b/starknet-macros/src/abigen/expand/enum.rs @@ -1,12 +1,13 @@ //! Enums expansion, taking in account generic types if any. use super::{ - generic, Expandable, - utils::{str_to_ident, str_to_type} + generic, + utils::{str_to_ident, str_to_type}, + Expandable, }; use starknet_contract::abi::parser::{ - CairoEnum, abi_types::{AbiType, AbiTypeAny}, + CairoEnum, }; use proc_macro2::TokenStream as TokenStream2; diff --git a/starknet-macros/src/abigen/expand/event.rs b/starknet-macros/src/abigen/expand/event.rs index e4a711cc..d6bb930e 100644 --- a/starknet-macros/src/abigen/expand/event.rs +++ b/starknet-macros/src/abigen/expand/event.rs @@ -1,12 +1,12 @@ //! Events expansion. use super::{ + utils::{str_to_ident, str_to_litstr, str_to_type}, Expandable, ExpandableEvent, - utils::{str_to_ident, str_to_litstr, str_to_type} }; use starknet_contract::abi::parser::{ + abi_types::{AbiType, AbiTypeAny}, CairoEvent, CairoEventInner, - abi_types::{AbiType, AbiTypeAny} }; use starknet_core::types::contract::EventFieldKind; diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index c5c366b7..0d565ad2 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -1,13 +1,13 @@ use super::{ - Expandable, utils::{str_to_ident, str_to_type}, + Expandable, }; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use starknet_contract::abi::parser::{ + abi_types::{AbiType, AbiTypeAny}, CairoFunction, - abi_types::{AbiType, AbiTypeAny} }; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; use starknet_core::types::contract::StateMutability; impl Expandable for CairoFunction { diff --git a/starknet-macros/src/abigen/expand/mod.rs b/starknet-macros/src/abigen/expand/mod.rs index 1aa80c77..4dbd9a10 100644 --- a/starknet-macros/src/abigen/expand/mod.rs +++ b/starknet-macros/src/abigen/expand/mod.rs @@ -6,8 +6,8 @@ pub(crate) mod generic; pub(crate) mod r#struct; pub(crate) mod utils; -use starknet_contract::abi::parser::CairoEvent; use proc_macro2::TokenStream as TokenStream2; +use starknet_contract::abi::parser::CairoEvent; pub trait Expandable { fn expand_decl(&self) -> TokenStream2; diff --git a/starknet-macros/src/abigen/expand/struct.rs b/starknet-macros/src/abigen/expand/struct.rs index c4368bc3..7f45a406 100644 --- a/starknet-macros/src/abigen/expand/struct.rs +++ b/starknet-macros/src/abigen/expand/struct.rs @@ -1,12 +1,13 @@ //! Struct expansion, taking in account generic types if any. use super::{ - generic, Expandable, - utils::{str_to_ident, str_to_type} + generic, + utils::{str_to_ident, str_to_type}, + Expandable, }; use starknet_contract::abi::parser::{ - CairoStruct, abi_types::{AbiType, AbiTypeAny}, + CairoStruct, }; use proc_macro2::TokenStream as TokenStream2; diff --git a/starknet-macros/src/abigen/mod.rs b/starknet-macros/src/abigen/mod.rs index 95091a70..7368b42c 100644 --- a/starknet-macros/src/abigen/mod.rs +++ b/starknet-macros/src/abigen/mod.rs @@ -10,14 +10,14 @@ //! checked for genericty to avoid duplicated types and detect correctly //! the members/variants that are generic. use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::parse_macro_input; -use proc_macro2::TokenStream as TokenStream2; use std::collections::HashMap; -use starknet_contract::abi::parser::{CairoEnum, CairoEvent, CairoFunction, CairoStruct}; use starknet_contract::abi::cairo_types::{CAIRO_BASIC_ENUMS, CAIRO_BASIC_STRUCTS}; +use starknet_contract::abi::parser::{CairoEnum, CairoEvent, CairoFunction, CairoStruct}; use starknet_core::types::contract::AbiEntry; mod expand; From f94c1c5e272ed8f35fe591ddc6b657db21fa3a56 Mon Sep 17 00:00:00 2001 From: glihm Date: Sun, 24 Sep 2023 00:28:20 -0600 Subject: [PATCH 06/31] fix some use to don't clash with main imports --- examples/abigen.rs | 3 +++ examples/abigen_events.rs | 4 ++-- starknet-macros/src/abigen/expand/contract.rs | 5 ---- starknet-macros/src/abigen/expand/enum.rs | 5 ++-- starknet-macros/src/abigen/expand/event.rs | 2 ++ starknet-macros/src/abigen/expand/function.rs | 24 ++++++++++++------- starknet-macros/src/abigen/expand/generic.rs | 4 ++-- starknet-macros/src/abigen/expand/struct.rs | 4 ++-- 8 files changed, 30 insertions(+), 21 deletions(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index 68142c9e..202c4014 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -11,6 +11,9 @@ use starknet::{ use std::sync::Arc; +// TODO: search where this one is missing...! +use starknet::contract::abi::cairo_types; + // Generate the bindings for the contract and also includes // all the structs and enums present in the ABI with the exact // same name. diff --git a/examples/abigen_events.rs b/examples/abigen_events.rs index 248d77c5..84b1ed3c 100644 --- a/examples/abigen_events.rs +++ b/examples/abigen_events.rs @@ -1,6 +1,6 @@ use starknet::{ - accounts::{ExecutionEncoding, SingleOwnerAccount}, - core::types::{EventFilter, FieldElement}, + accounts::{Account, ExecutionEncoding, SingleOwnerAccount}, + core::types::{BlockId, BlockTag, EventFilter, FieldElement}, macros::{abigen, felt}, providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider}, signers::{LocalWallet, SigningKey}, diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index 8109fab0..b89378d9 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -11,11 +11,6 @@ impl CairoContract { pub fn expand(contract_name: Ident) -> TokenStream2 { quote! { - use starknet::contract::abi::cairo_types::{self, Error as CairoError}; - use starknet::contract::abi::CairoType; - use starknet::core::types::{BlockId, BlockTag}; - use starknet::accounts::Account; - #[derive(Debug)] pub struct #contract_name

where diff --git a/starknet-macros/src/abigen/expand/enum.rs b/starknet-macros/src/abigen/expand/enum.rs index 2b78cb9e..9d47c06e 100644 --- a/starknet-macros/src/abigen/expand/enum.rs +++ b/starknet-macros/src/abigen/expand/enum.rs @@ -106,7 +106,7 @@ impl Expandable for CairoEnum { let impl_line = if self.is_generic() { generic::impl_with_gentys_tokens(&enum_name, &gentys) } else { - quote!(impl cairo_types::CairoType for #enum_name) + quote!(impl starknet::contract::abi::CairoType for #enum_name) }; let rust_type = if self.is_generic() { @@ -138,7 +138,8 @@ impl Expandable for CairoEnum { } } - fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> cairo_types::Result { + fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> starknet::contract::abi::cairo_types::Result { + let index:u128 = felts[offset].try_into().unwrap(); match index as usize { #(#deserializations),* diff --git a/starknet-macros/src/abigen/expand/event.rs b/starknet-macros/src/abigen/expand/event.rs index d6bb930e..81e0a4d0 100644 --- a/starknet-macros/src/abigen/expand/event.rs +++ b/starknet-macros/src/abigen/expand/event.rs @@ -176,6 +176,8 @@ impl ExpandableEvent for CairoEvent { type Error = String; fn try_from(event: starknet::core::types::EmittedEvent) -> Result { + use starknet::contract::abi::CairoType; + if event.keys.is_empty() { return Err("Missing event selector, no keys found".to_string()); } diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 0d565ad2..740d85a3 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -26,7 +26,7 @@ impl Expandable for CairoFunction { StateMutability::View => match &self.output { Some(o) => { let oty = str_to_type(&o.to_rust_type()); - quote!(-> cairo_types::Result<#oty>) + quote!(-> starknet::contract::abi::cairo_types::Result<#oty>) } None => quote!(), }, @@ -80,6 +80,10 @@ impl Expandable for CairoFunction { match &self.state_mutability { StateMutability::View => quote! { #decl { + use starknet::contract::abi::cairo_types::{self, Error as CairoError}; + use starknet::contract::abi::CairoType; + use starknet::core::types::{BlockId, BlockTag}; + let mut calldata = vec![]; #(#serializations)* @@ -94,7 +98,7 @@ impl Expandable for CairoFunction { ) .await.map_err( |err| - cairo_types::Error::Deserialize( + starknet::contract::abi::cairo_types::Error::Deserialize( format!("Deserialization error {}", err)))?; #out_res @@ -110,6 +114,10 @@ impl Expandable for CairoFunction { // The estimate only may be done at the function level, to avoid // altering the contract instance itself and hence races. #decl { + use starknet::contract::abi::cairo_types::{self, Error as CairoError}; + use starknet::contract::abi::CairoType; + use starknet::accounts::Account; + // TODO: I don't know how to easily store the SingleOwnerAccount // and it's generic types without complexifiying the whole typing. // So it's constructed at every call. There is surely a better approach. @@ -145,12 +153,12 @@ impl Expandable for CairoFunction { #[cfg(test)] mod tests { use crate::Expandable; - use cairo_type_parser::{ + use proc_macro2::TokenStream2; + use quote::quote; + use starknet::contract::abi::parser::{ abi_types::{AbiType, AbiTypeAny}, CairoFunction, }; - use proc_macro2::TokenStream2; - use quote::quote; use starknet::core::types::contract::StateMutability; #[test] @@ -166,7 +174,7 @@ mod tests { }; let te1 = cf.expand_decl(); let tef1: TokenStream2 = quote!( - pub async fn my_func(&self, v1: &starknet::core::types::FieldElement, v2: &starknet::core::types::FieldElement) -> cairo_types::Result + pub async fn my_func(&self, v1: &starknet::core::types::FieldElement, v2: &starknet::core::types::FieldElement) -> starknet::contract::abi::cairo_types::Result ); assert_eq!(te1.to_string(), tef1.to_string()); @@ -191,7 +199,7 @@ mod tests { &self, v1: &starknet::core::types::FieldElement, v2: &starknet::core::types::FieldElement - ) -> cairo_types::Result { + ) -> starknet::contract::abi::cairo_types::Result { let mut calldata = vec![]; calldata.extend(starknet::core::types::FieldElement::serialize(v1)); calldata.extend(starknet::core::types::FieldElement::serialize(v2)); @@ -205,7 +213,7 @@ mod tests { }, BlockId::Tag(BlockTag::Pending), ) - .await.map_err(|err| cairo_types::Error::Deserialize(format!("Deserialization error {:}" , err)))?; + .await.map_err(|err| starknet::contract::abi::cairo_types::Error::Deserialize(format!("Deserialization error {:}" , err)))?; starknet::core::types::FieldElement::deserialize(&r, 0) } diff --git a/starknet-macros/src/abigen/expand/generic.rs b/starknet-macros/src/abigen/expand/generic.rs index 48ce2b6f..8dd3831d 100644 --- a/starknet-macros/src/abigen/expand/generic.rs +++ b/starknet-macros/src/abigen/expand/generic.rs @@ -15,13 +15,13 @@ pub fn impl_with_gentys_tokens(entity_name: &Ident, gentys: &Vec) -> Toke let mut tokens = vec![]; tokens.push(quote! { - impl<#(#gentys),* , #(#gentys_rust),*> cairo_types::CairoType for #entity_name<#(#gentys),*> + impl<#(#gentys),* , #(#gentys_rust),*> starknet::contract::abi::CairoType for #entity_name<#(#gentys),*> where }); for (i, g) in gentys.iter().enumerate() { let gr = &gentys_rust[i]; - tokens.push(quote!(#g: CairoType,)); + tokens.push(quote!(#g: starknet::contract::abi::CairoType,)); } quote!(#(#tokens)*) diff --git a/starknet-macros/src/abigen/expand/struct.rs b/starknet-macros/src/abigen/expand/struct.rs index 7f45a406..b30fad46 100644 --- a/starknet-macros/src/abigen/expand/struct.rs +++ b/starknet-macros/src/abigen/expand/struct.rs @@ -87,7 +87,7 @@ impl Expandable for CairoStruct { let impl_line = if self.is_generic() { generic::impl_with_gentys_tokens(&struct_name, &gentys) } else { - quote!(impl cairo_types::CairoType for #struct_name) + quote!(impl starknet::contract::abi::CairoType for #struct_name) }; let rust_type = if self.is_generic() { @@ -116,7 +116,7 @@ impl Expandable for CairoStruct { out } - fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> cairo_types::Result { + fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> starknet::contract::abi::cairo_types::Result { let mut offset = offset; #(#desers)* Ok(#struct_name { From fa5b9b82f945785d83b081740aa2ebb5623013f6 Mon Sep 17 00:00:00 2001 From: glihm Date: Sun, 24 Sep 2023 00:48:19 -0600 Subject: [PATCH 07/31] fix imports --- Cargo.lock | 795 ++++++++++-------- examples/abigen.rs | 3 - .../src/abi/parser/abi_types/basic.rs | 6 +- starknet-macros/src/abigen/expand/enum.rs | 2 - starknet-macros/src/abigen/expand/function.rs | 22 +- 5 files changed, 446 insertions(+), 382 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c85e1d9..1e0a46f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -10,15 +19,30 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -100,19 +124,19 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] @@ -128,9 +152,9 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", @@ -144,6 +168,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -152,15 +191,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bigdecimal" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" dependencies = [ "num-bigint", "num-integer", @@ -197,9 +236,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-slice-cast" @@ -215,9 +254,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cast" @@ -227,9 +266,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -239,22 +281,22 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", - "num-integer", "num-traits", "serde", - "winapi", + "windows-targets", ] [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", @@ -263,15 +305,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", @@ -289,13 +331,13 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags", "clap_lex", - "indexmap", + "indexmap 1.9.3", "textwrap", ] @@ -336,9 +378,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -394,9 +436,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array", "subtle", @@ -424,9 +466,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.94" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "bbe98ba1789d56fb3db3bee5e032774d4f421b685de7ba703643584ba24effbe" dependencies = [ "cc", "cxxbridge-flags", @@ -436,9 +478,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.94" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +checksum = "c4ce20f6b8433da4841b1dadfb9468709868022d829d5ca1f2ffbda928455ea3" dependencies = [ "cc", "codespan-reporting", @@ -446,31 +488,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] name = "cxxbridge-flags" -version = "1.0.94" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" +checksum = "20888d9e1d2298e2ff473cee30efe7d5036e437857ab68bbfea84c74dba91da2" [[package]] name = "cxxbridge-macro" -version = "1.0.94" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] name = "darling" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -478,27 +520,36 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn 2.0.37", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.37", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +dependencies = [ + "serde", ] [[package]] @@ -514,9 +565,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -525,19 +576,25 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "eth-keystore" version = "0.5.0" @@ -601,9 +658,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide", @@ -617,9 +674,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -645,6 +702,17 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -664,9 +732,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -681,9 +751,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "js-sys", @@ -692,11 +762,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -704,7 +780,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -723,6 +799,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -734,12 +816,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -792,15 +871,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -813,7 +892,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -822,10 +901,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", "rustls", @@ -835,9 +915,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -849,12 +929,11 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -865,9 +944,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -918,10 +997,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "inout" version = "0.1.3" @@ -933,9 +1022,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itertools" @@ -948,24 +1037,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -978,24 +1067,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "link-cplusplus" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ "cc", ] [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -1003,15 +1092,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "mime" @@ -1021,30 +1110,29 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -1063,28 +1151,37 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.3", "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -1094,15 +1191,15 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" dependencies = [ "arrayvec", "bitvec", @@ -1114,9 +1211,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1136,22 +1233,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-targets", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -1164,15 +1261,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1235,18 +1332,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1289,35 +1386,49 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.0", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -1385,6 +1496,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -1402,30 +1519,40 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.4", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ - "base64 0.21.0", + "ring", + "untrusted", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa20" @@ -1453,15 +1580,15 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "scrypt" @@ -1487,35 +1614,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1547,14 +1674,14 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" dependencies = [ "base64 0.13.1", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", "serde", "serde_json", "serde_with_macros", @@ -1563,21 +1690,21 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.37", ] [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -1586,9 +1713,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest", "keccak", @@ -1605,18 +1732,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -1628,6 +1755,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spin" version = "0.5.2" @@ -1689,7 +1826,7 @@ dependencies = [ name = "starknet-core" version = "0.6.0" dependencies = [ - "base64 0.21.0", + "base64 0.21.4", "criterion", "flate2", "hex", @@ -1733,7 +1870,7 @@ version = "0.3.2" dependencies = [ "starknet-curve", "starknet-ff", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] @@ -1776,7 +1913,7 @@ dependencies = [ "serde_json", "starknet-contract", "starknet-core", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] @@ -1838,9 +1975,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -1855,9 +1992,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -1872,9 +2009,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] @@ -1887,30 +2024,31 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] name = "time" -version = "0.3.20" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ + "deranged", "itoa", "serde", "time-core", @@ -1919,15 +2057,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -1968,11 +2106,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -1980,38 +2118,37 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", - "webpki", ] [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -2023,17 +2160,17 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] @@ -2057,9 +2194,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -2072,9 +2209,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "uint" @@ -2096,9 +2233,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2111,9 +2248,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "untrusted" @@ -2123,9 +2260,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -2150,9 +2287,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2160,11 +2297,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2176,9 +2312,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2186,24 +2322,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.37", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -2213,9 +2349,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2223,28 +2359,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-bindgen-test" -version = "0.3.34" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db36fc0f9fb209e88fb3642590ae0205bb5a56216dabd963ba15879fe53a30b" +checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" dependencies = [ "console_error_panic_hook", "js-sys", @@ -2256,9 +2392,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.34" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0734759ae6b3b1717d661fe4f016efcfb9828f5edb4520c18eaee05af3b43be9" +checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ "proc-macro2", "quote", @@ -2266,32 +2402,19 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "winapi" @@ -2311,9 +2434,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -2330,148 +2453,92 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.4.1" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys", ] [[package]] @@ -2500,5 +2567,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] diff --git a/examples/abigen.rs b/examples/abigen.rs index 202c4014..68142c9e 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -11,9 +11,6 @@ use starknet::{ use std::sync::Arc; -// TODO: search where this one is missing...! -use starknet::contract::abi::cairo_types; - // Generate the bindings for the contract and also includes // all the structs and enums present in the ABI with the exact // same name. diff --git a/starknet-contract/src/abi/parser/abi_types/basic.rs b/starknet-contract/src/abi/parser/abi_types/basic.rs index 004efb91..8b78c83f 100644 --- a/starknet-contract/src/abi/parser/abi_types/basic.rs +++ b/starknet-contract/src/abi/parser/abi_types/basic.rs @@ -26,9 +26,9 @@ impl AbiBasic { let s = self.get_cairo_type_name(); match s.as_str() { "felt252" => "starknet::core::types::FieldElement".to_string(), - "ContractAddress" => "cairo_types::types::starknet::ContractAddress".to_string(), - "ClassHash" => "cairo_types::types::starknet::ClassHash".to_string(), - "EthAddress" => "cairo_types::types::starknet::EthAddress".to_string(), + "ContractAddress" => "starknet::contract::abi::cairo_types::ContractAddress".to_string(), + "ClassHash" => "starknet::contract::abi::cairo_types::ClassHash".to_string(), + "EthAddress" => "starknet::contract::abi::cairo_types::EthAddress".to_string(), _ => s.clone(), } } diff --git a/starknet-macros/src/abigen/expand/enum.rs b/starknet-macros/src/abigen/expand/enum.rs index 9d47c06e..22b221db 100644 --- a/starknet-macros/src/abigen/expand/enum.rs +++ b/starknet-macros/src/abigen/expand/enum.rs @@ -132,14 +132,12 @@ impl Expandable for CairoEnum { } fn serialize(rust: &Self::RustType) -> Vec { - match rust { #(#serializations),* } } fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> starknet::contract::abi::cairo_types::Result { - let index:u128 = felts[offset].try_into().unwrap(); match index as usize { #(#deserializations),* diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 740d85a3..d39b555a 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -21,7 +21,7 @@ impl Expandable for CairoFunction { // We can pass a reference here as serialize always takes a reference. inputs.push(quote!(#name:&#ty)); } - + let output = match self.state_mutability { StateMutability::View => match &self.output { Some(o) => { @@ -80,7 +80,6 @@ impl Expandable for CairoFunction { match &self.state_mutability { StateMutability::View => quote! { #decl { - use starknet::contract::abi::cairo_types::{self, Error as CairoError}; use starknet::contract::abi::CairoType; use starknet::core::types::{BlockId, BlockTag}; @@ -94,7 +93,7 @@ impl Expandable for CairoFunction { entry_point_selector: starknet::macros::selector!(#func_name), calldata, }, - BlockId::Tag(BlockTag::Pending), + BlockId::Tag(BlockTag::Latest), ) .await.map_err( |err| @@ -114,7 +113,6 @@ impl Expandable for CairoFunction { // The estimate only may be done at the function level, to avoid // altering the contract instance itself and hence races. #decl { - use starknet::contract::abi::cairo_types::{self, Error as CairoError}; use starknet::contract::abi::CairoType; use starknet::accounts::Account; @@ -152,14 +150,14 @@ impl Expandable for CairoFunction { #[cfg(test)] mod tests { - use crate::Expandable; - use proc_macro2::TokenStream2; + use crate::abigen::Expandable; + use proc_macro2::TokenStream as TokenStream2; use quote::quote; - use starknet::contract::abi::parser::{ - abi_types::{AbiType, AbiTypeAny}, + use starknet_contract::abi::parser::{ + abi_types::AbiTypeAny, CairoFunction, }; - use starknet::core::types::contract::StateMutability; + use starknet_core::types::contract::StateMutability; #[test] fn test_decl_basic() { @@ -200,6 +198,10 @@ mod tests { v1: &starknet::core::types::FieldElement, v2: &starknet::core::types::FieldElement ) -> starknet::contract::abi::cairo_types::Result { + use starknet::contract::abi::cairo_types::{self, Error as CairoError}; + use starknet::contract::abi::CairoType; + use starknet::core::types::{BlockId, BlockTag}; + let mut calldata = vec![]; calldata.extend(starknet::core::types::FieldElement::serialize(v1)); calldata.extend(starknet::core::types::FieldElement::serialize(v2)); @@ -211,7 +213,7 @@ mod tests { entry_point_selector: starknet::macros::selector!("my_func"), calldata, }, - BlockId::Tag(BlockTag::Pending), + BlockId::Tag(BlockTag::Latest), ) .await.map_err(|err| starknet::contract::abi::cairo_types::Error::Deserialize(format!("Deserialization error {:}" , err)))?; From 2942185735d42374002c21bbf205ce2e5d28ee84 Mon Sep 17 00:00:00 2001 From: glihm Date: Sun, 24 Sep 2023 00:48:31 -0600 Subject: [PATCH 08/31] cargo fmt --- starknet-contract/src/abi/parser/abi_types/basic.rs | 4 +++- starknet-macros/src/abigen/expand/function.rs | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/starknet-contract/src/abi/parser/abi_types/basic.rs b/starknet-contract/src/abi/parser/abi_types/basic.rs index 8b78c83f..4913092a 100644 --- a/starknet-contract/src/abi/parser/abi_types/basic.rs +++ b/starknet-contract/src/abi/parser/abi_types/basic.rs @@ -26,7 +26,9 @@ impl AbiBasic { let s = self.get_cairo_type_name(); match s.as_str() { "felt252" => "starknet::core::types::FieldElement".to_string(), - "ContractAddress" => "starknet::contract::abi::cairo_types::ContractAddress".to_string(), + "ContractAddress" => { + "starknet::contract::abi::cairo_types::ContractAddress".to_string() + } "ClassHash" => "starknet::contract::abi::cairo_types::ClassHash".to_string(), "EthAddress" => "starknet::contract::abi::cairo_types::EthAddress".to_string(), _ => s.clone(), diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index d39b555a..cd5f1e82 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -21,7 +21,7 @@ impl Expandable for CairoFunction { // We can pass a reference here as serialize always takes a reference. inputs.push(quote!(#name:&#ty)); } - + let output = match self.state_mutability { StateMutability::View => match &self.output { Some(o) => { @@ -153,10 +153,7 @@ mod tests { use crate::abigen::Expandable; use proc_macro2::TokenStream as TokenStream2; use quote::quote; - use starknet_contract::abi::parser::{ - abi_types::AbiTypeAny, - CairoFunction, - }; + use starknet_contract::abi::parser::{abi_types::AbiTypeAny, CairoFunction}; use starknet_core::types::contract::StateMutability; #[test] From b16941c5b9730f4bda3d380123a3a7348fe7fda0 Mon Sep 17 00:00:00 2001 From: glihm Date: Sun, 24 Sep 2023 00:55:04 -0600 Subject: [PATCH 09/31] change print location for example --- examples/abigen.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index 68142c9e..b7db8a53 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -31,6 +31,8 @@ async fn main() { .await .expect("Call to get balance failed"); + println!("Your ETH (goerli) balance: {:?}", balance); + // For the inputs / outputs of the ABI functions, all the types are // defined where the abigen! macro is expanded. Note that `u256` // for the balance were already in the scope as it's generated from @@ -58,6 +60,4 @@ async fn main() { high: 0, }, ); - - println!("Your balance: {:?}", balance); } From 9ec158e34a4e6d7c826c79a339d573cd35b3f9b4 Mon Sep 17 00:00:00 2001 From: glihm Date: Mon, 25 Sep 2023 11:19:52 -0600 Subject: [PATCH 10/31] clippy revision --- examples/abigen.rs | 30 +++++++++++-------- examples/abigen_events.rs | 10 +++---- .../src/abi/cairo_types/types/boolean.rs | 6 ++-- .../src/abi/cairo_types/types/integers.rs | 12 ++++---- .../src/abi/parser/abi_types/array.rs | 8 ++--- .../src/abi/parser/abi_types/generic.rs | 8 ++--- .../src/abi/parser/abi_types/tuple.rs | 13 ++++---- 7 files changed, 45 insertions(+), 42 deletions(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index b7db8a53..d5663e29 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -4,7 +4,7 @@ use starknet::{ accounts::{ExecutionEncoding, SingleOwnerAccount}, contract::abi::ContractAddress, core::{chain_id, types::FieldElement}, - macros::{abigen, felt}, + macros::abigen, providers::{Provider, SequencerGatewayProvider}, signers::{LocalWallet, SigningKey}, }; @@ -19,15 +19,19 @@ abigen!(TokenContract, "./examples/contracts_abis/mini_erc20.json"); #[tokio::main] async fn main() { let provider = Arc::new(SequencerGatewayProvider::starknet_alpha_goerli()); - let eth_goerli_token_address = - felt!("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"); + let eth_goerli_token_address = FieldElement::from_hex_be( + "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + ) + .unwrap(); let token_contract = TokenContract::new(eth_goerli_token_address, Arc::clone(&provider)); // To call a view, there is no need to initialize an account. You can directly // use the name of the method in the ABI to realize the call. let balance: u256 = token_contract - .balanceOf(&ContractAddress(felt!("YOUR_ACCOUNT_ADDRESS_HEX_HERE"))) + .balanceOf(&ContractAddress( + FieldElement::from_hex_be("YOUR_ACCOUNT_ADDRESS_HEX_HERE").unwrap(), + )) .await .expect("Call to get balance failed"); @@ -43,7 +47,7 @@ async fn main() { FieldElement::from_hex_be("YOUR_PRIVATE_KEY_IN_HEX_HERE").unwrap(), )); let address = FieldElement::from_hex_be("YOUR_ACCOUNT_CONTRACT_ADDRESS_IN_HEX_HERE").unwrap(); - let mut account = SingleOwnerAccount::new( + let account = SingleOwnerAccount::new( provider, signer, address, @@ -53,11 +57,13 @@ async fn main() { let token_contract = token_contract.with_account(Arc::new(account)); - token_contract.approve( - &ContractAddress(felt!("SPENDER_ADDRESS_HEX")), - &u256 { - low: 10000, - high: 0, - }, - ); + let _ = token_contract + .approve( + &ContractAddress(FieldElement::from_hex_be("SPENDER_ADDRESS_HEX").unwrap()), + &u256 { + low: 10000, + high: 0, + }, + ) + .await; } diff --git a/examples/abigen_events.rs b/examples/abigen_events.rs index 84b1ed3c..6fde356b 100644 --- a/examples/abigen_events.rs +++ b/examples/abigen_events.rs @@ -1,5 +1,5 @@ use starknet::{ - accounts::{Account, ExecutionEncoding, SingleOwnerAccount}, + accounts::{ExecutionEncoding, SingleOwnerAccount}, core::types::{BlockId, BlockTag, EventFilter, FieldElement}, macros::{abigen, felt}, providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider}, @@ -23,14 +23,14 @@ async fn main() { )); let address = FieldElement::from_hex_be("YOUR_ACCOUNT_CONTRACT_ADDRESS_IN_HEX_HERE").unwrap(); let account = SingleOwnerAccount::new( - provider, + provider.clone(), signer, address, felt!("0x4b4154414e41"), // KATANA ExecutionEncoding::Legacy, ); - let contract_address = felt!("CONTRACT_ADDRESS_HEX"); + let contract_address = FieldElement::from_hex_be("CONTRACT_ADDRESS_HEX").unwrap(); let event_contract = Contract::new(contract_address, Arc::clone(&provider)).with_account(Arc::new(account)); @@ -78,10 +78,10 @@ async fn main() { // is automatically done based on the variant // from the event keys and data. match my_event { - Event::MyEventA(a) => { + Event::MyEventA(_a) => { // do stuff with a.header and a.value. } - Event::MyEventB(b) => { + Event::MyEventB(_b) => { // do stuff with b.value. } }; diff --git a/starknet-contract/src/abi/cairo_types/types/boolean.rs b/starknet-contract/src/abi/cairo_types/types/boolean.rs index 964ec889..056ca489 100644 --- a/starknet-contract/src/abi/cairo_types/types/boolean.rs +++ b/starknet-contract/src/abi/cairo_types/types/boolean.rs @@ -38,8 +38,8 @@ mod tests { #[test] fn test_deserialize_bool() { let felts = vec![FieldElement::ZERO, FieldElement::ONE, FieldElement::TWO]; - assert_eq!(bool::deserialize(&felts, 0).unwrap(), false); - assert_eq!(bool::deserialize(&felts, 1).unwrap(), true); - assert_eq!(bool::deserialize(&felts, 2).unwrap(), false); + assert!(!bool::deserialize(&felts, 0).unwrap()); + assert!(bool::deserialize(&felts, 1).unwrap()); + assert!(!bool::deserialize(&felts, 2).unwrap()); } } diff --git a/starknet-contract/src/abi/cairo_types/types/integers.rs b/starknet-contract/src/abi/cairo_types/types/integers.rs index 71b62ee8..4bc829f7 100644 --- a/starknet-contract/src/abi/cairo_types/types/integers.rs +++ b/starknet-contract/src/abi/cairo_types/types/integers.rs @@ -58,7 +58,7 @@ mod tests { let v = 12_u8; let felts = u8::serialize(&v); assert_eq!(felts.len(), 1); - assert_eq!(felts[0], FieldElement::from(12 as u8)); + assert_eq!(felts[0], FieldElement::from(12_u8)); } #[test] @@ -73,7 +73,7 @@ mod tests { let v = 12_u16; let felts = u16::serialize(&v); assert_eq!(felts.len(), 1); - assert_eq!(felts[0], FieldElement::from(12 as u16)); + assert_eq!(felts[0], FieldElement::from(12_u16)); } #[test] @@ -88,7 +88,7 @@ mod tests { let v = 123_u32; let felts = u32::serialize(&v); assert_eq!(felts.len(), 1); - assert_eq!(felts[0], FieldElement::from(123 as u32)); + assert_eq!(felts[0], FieldElement::from(123_u32)); } #[test] @@ -103,7 +103,7 @@ mod tests { let v = 123_u64; let felts = u64::serialize(&v); assert_eq!(felts.len(), 1); - assert_eq!(felts[0], FieldElement::from(123 as u64)); + assert_eq!(felts[0], FieldElement::from(123_u64)); } #[test] @@ -118,7 +118,7 @@ mod tests { let v = 123_u128; let felts = u128::serialize(&v); assert_eq!(felts.len(), 1); - assert_eq!(felts[0], FieldElement::from(123 as u128)); + assert_eq!(felts[0], FieldElement::from(123_u128)); } #[test] @@ -133,7 +133,7 @@ mod tests { let v = 123; let felts = usize::serialize(&v); assert_eq!(felts.len(), 1); - assert_eq!(felts[0], FieldElement::from(123 as u128)); + assert_eq!(felts[0], FieldElement::from(123_u128)); } #[test] diff --git a/starknet-contract/src/abi/parser/abi_types/array.rs b/starknet-contract/src/abi/parser/abi_types/array.rs index b3afa5af..71fac9bc 100644 --- a/starknet-contract/src/abi/parser/abi_types/array.rs +++ b/starknet-contract/src/abi/parser/abi_types/array.rs @@ -87,12 +87,12 @@ impl AbiType for AbiArray { #[cfg(test)] mod tests { use super::*; - use crate::abi::parser::abi_types::{AbiBasic, AbiTypeAny}; + use crate::abi::parser::abi_types::AbiTypeAny; fn get_default() -> AbiArray { AbiArray::new( "core::array::Array", - AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), + AbiTypeAny::Basic("core::felt252".into()), ) } @@ -142,8 +142,8 @@ mod tests { "core::array::Array", AbiTypeAny::Tuple( vec![ - AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), - AbiTypeAny::Basic(AbiBasic::new("core::integer::u32".into())), + AbiTypeAny::Basic("core::felt252".into()), + AbiTypeAny::Basic("core::integer::u32".into()), ] .into() ) diff --git a/starknet-contract/src/abi/parser/abi_types/generic.rs b/starknet-contract/src/abi/parser/abi_types/generic.rs index 3830129a..220c1e62 100644 --- a/starknet-contract/src/abi/parser/abi_types/generic.rs +++ b/starknet-contract/src/abi/parser/abi_types/generic.rs @@ -167,12 +167,12 @@ impl AbiType for AbiGeneric { #[cfg(test)] mod tests { use super::*; - use crate::abi::parser::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; + use crate::abi::parser::abi_types::{AbiArray, AbiTypeAny}; fn get_default() -> AbiGeneric { AbiGeneric::new( "contract1::MyStruct", - vec![AbiTypeAny::Basic(AbiBasic::new("core::felt252".into()))], + vec![AbiTypeAny::Basic("core::felt252".into())], ) } @@ -180,8 +180,8 @@ mod tests { AbiGeneric::new( "contract1::MyStruct", vec![ - AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), - AbiTypeAny::Basic(AbiBasic::new("core::integer::u32".into())), + AbiTypeAny::Basic("core::felt252".into()), + AbiTypeAny::Basic("core::integer::u32".into()), ], ) } diff --git a/starknet-contract/src/abi/parser/abi_types/tuple.rs b/starknet-contract/src/abi/parser/abi_types/tuple.rs index dd7ac252..500a8df5 100644 --- a/starknet-contract/src/abi/parser/abi_types/tuple.rs +++ b/starknet-contract/src/abi/parser/abi_types/tuple.rs @@ -127,16 +127,13 @@ impl AbiType for AbiTuple { #[cfg(test)] mod tests { use super::*; - use crate::abi::parser::abi_types::{AbiArray, AbiBasic, AbiTypeAny}; + use crate::abi::parser::abi_types::{AbiArray, AbiTypeAny}; fn get_default() -> AbiTuple { - AbiTuple::new( - vec![ - AbiTypeAny::Basic(AbiBasic::new("core::felt252".into())), - AbiTypeAny::Basic(AbiBasic::new("core::integer::u32".into())), - ] - .into(), - ) + AbiTuple::new(vec![ + AbiTypeAny::Basic("core::felt252".into()), + AbiTypeAny::Basic("core::integer::u32".into()), + ]) } #[test] From 00474cce5c71e923a1ff4b7d6c8d018313a859e6 Mon Sep 17 00:00:00 2001 From: glihm Date: Mon, 25 Sep 2023 11:26:05 -0600 Subject: [PATCH 11/31] fix typo for abigen function test --- starknet-macros/src/abigen/expand/function.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index cd5f1e82..df559e7d 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -195,7 +195,6 @@ mod tests { v1: &starknet::core::types::FieldElement, v2: &starknet::core::types::FieldElement ) -> starknet::contract::abi::cairo_types::Result { - use starknet::contract::abi::cairo_types::{self, Error as CairoError}; use starknet::contract::abi::CairoType; use starknet::core::types::{BlockId, BlockTag}; @@ -212,7 +211,7 @@ mod tests { }, BlockId::Tag(BlockTag::Latest), ) - .await.map_err(|err| starknet::contract::abi::cairo_types::Error::Deserialize(format!("Deserialization error {:}" , err)))?; + .await.map_err(|err| starknet::contract::abi::cairo_types::Error::Deserialize(format!("Deserialization error {}" , err)))?; starknet::core::types::FieldElement::deserialize(&r, 0) } From a26aeb21f60868a0578144f14ef089b4d3a7f785 Mon Sep 17 00:00:00 2001 From: glihm Date: Mon, 25 Sep 2023 12:00:54 -0600 Subject: [PATCH 12/31] add ptr_arg ignore clippy As we need the type match for deserialization, the item path is expecting , which fails if we pass a slice --- starknet-macros/src/abigen/expand/function.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index df559e7d..2ba249a9 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -79,6 +79,7 @@ impl Expandable for CairoFunction { match &self.state_mutability { StateMutability::View => quote! { + #[allow(clippy::ptr_arg)] #decl { use starknet::contract::abi::CairoType; use starknet::core::types::{BlockId, BlockTag}; @@ -112,6 +113,7 @@ impl Expandable for CairoFunction { // fees (manual, estimated + scale factor). // The estimate only may be done at the function level, to avoid // altering the contract instance itself and hence races. + #[allow(clippy::ptr_arg)] #decl { use starknet::contract::abi::CairoType; use starknet::accounts::Account; From bb97020b6ca0abf2541585fb0ab7d9e4d5d315f9 Mon Sep 17 00:00:00 2001 From: glihm Date: Mon, 25 Sep 2023 20:22:30 -0600 Subject: [PATCH 13/31] fix missing macro in expansion --- starknet-macros/src/abigen/expand/function.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 2ba249a9..4d8df42f 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -192,6 +192,7 @@ mod tests { #[rustfmt::skip] let tef1: TokenStream2 = quote!( + #[allow(clippy::ptr_arg)] pub async fn my_func( &self, v1: &starknet::core::types::FieldElement, From f328b29ee7e948b8c4f5b9e0da8327fdddd833bf Mon Sep 17 00:00:00 2001 From: glihm Date: Wed, 27 Sep 2023 08:09:52 -0600 Subject: [PATCH 14/31] add support for interfaces interfaces define some functions that are exposed by the contract. Can it be interesting to split them? --- starknet-macros/src/abigen/mod.rs | 98 ++++++++++++++++++------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/starknet-macros/src/abigen/mod.rs b/starknet-macros/src/abigen/mod.rs index 7368b42c..096f532d 100644 --- a/starknet-macros/src/abigen/mod.rs +++ b/starknet-macros/src/abigen/mod.rs @@ -42,48 +42,7 @@ pub fn abigen_internal(input: TokenStream) -> TokenStream { let mut events = vec![]; for entry in &abi { - match entry { - AbiEntry::Struct(s) => { - let cs = CairoStruct::new(&s.name, &s.members); - - if CAIRO_BASIC_STRUCTS.contains(&cs.get_name().as_str()) { - continue; - } - - if let Some(ref mut existing_cs) = structs.get_mut(&cs.get_name()) { - cs.compare_generic_types(existing_cs); - } else { - structs.insert(cs.get_name(), cs.clone()); - } - } - AbiEntry::Enum(e) => { - let ce = CairoEnum::new(&e.name, &e.variants); - - if CAIRO_BASIC_ENUMS.contains(&ce.get_name().as_str()) { - continue; - } - - if let Some(ref mut existing_ce) = enums.get_mut(&ce.get_name()) { - ce.compare_generic_types(existing_ce); - } else { - enums.insert(ce.get_name(), ce.clone()); - } - } - AbiEntry::Function(f) => { - // Functions cannot be generic when they are entry point. - // From this statement, we can safely assume that any function name is - // unique. - let cf = - CairoFunction::new(&f.name, f.state_mutability.clone(), &f.inputs, &f.outputs); - functions.push(cf.expand_impl()); - } - AbiEntry::Event(ev) => { - if let Some(cev) = CairoEvent::new(ev) { - events.push(cev); - } - } - _ => continue, - } + parse_entry(entry, &mut structs, &mut enums, &mut functions, &mut events); } for (_, cs) in structs { @@ -116,3 +75,58 @@ pub fn abigen_internal(input: TokenStream) -> TokenStream { expanded.into() } + +fn parse_entry( + entry: &AbiEntry, + structs: &mut HashMap, + enums: &mut HashMap, + functions: &mut Vec, + events: &mut Vec, +) { + match entry { + AbiEntry::Struct(s) => { + let cs = CairoStruct::new(&s.name, &s.members); + + if CAIRO_BASIC_STRUCTS.contains(&cs.get_name().as_str()) { + return; + } + + if let Some(ref mut existing_cs) = structs.get_mut(&cs.get_name()) { + cs.compare_generic_types(existing_cs); + } else { + structs.insert(cs.get_name(), cs.clone()); + } + } + AbiEntry::Enum(e) => { + let ce = CairoEnum::new(&e.name, &e.variants); + + if CAIRO_BASIC_ENUMS.contains(&ce.get_name().as_str()) { + return; + } + + if let Some(ref mut existing_ce) = enums.get_mut(&ce.get_name()) { + ce.compare_generic_types(existing_ce); + } else { + enums.insert(ce.get_name(), ce.clone()); + } + } + AbiEntry::Function(f) => { + // Functions cannot be generic when they are entry point. + // From this statement, we can safely assume that any function name is + // unique. + let cf = CairoFunction::new(&f.name, f.state_mutability.clone(), &f.inputs, &f.outputs); + functions.push(cf.expand_impl()); + } + AbiEntry::Event(ev) => { + if let Some(cev) = CairoEvent::new(ev) { + events.push(cev); + } + } + AbiEntry::Interface(interface) => { + for entry in &interface.items { + parse_entry(entry, structs, enums, functions, events); + } + } + _ => (), + } +} From f79f9e69ef9858360f36e6ef6bc0b66d67204078 Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 7 Oct 2023 22:05:40 -0600 Subject: [PATCH 15/31] fix tuple parsing with nested generic types --- .../src/abi/parser/abi_types/array.rs | 9 ++++++++ .../src/abi/parser/abi_types/mod.rs | 21 +++++++------------ .../src/abi/parser/abi_types/tuple.rs | 10 +++++++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/starknet-contract/src/abi/parser/abi_types/array.rs b/starknet-contract/src/abi/parser/abi_types/array.rs index 71fac9bc..dcdabf02 100644 --- a/starknet-contract/src/abi/parser/abi_types/array.rs +++ b/starknet-contract/src/abi/parser/abi_types/array.rs @@ -186,4 +186,13 @@ mod tests { ("core::array::Array::".to_string(), false) ); } + + #[test] + fn array_in_array_not_generic() { + let t = AbiTypeAny::from_string("core::array::Span::>"); + assert_eq!( + t.to_rust_type(), + "Vec>" + ); + } } diff --git a/starknet-contract/src/abi/parser/abi_types/mod.rs b/starknet-contract/src/abi/parser/abi_types/mod.rs index 46d3d1c8..e248bbcb 100644 --- a/starknet-contract/src/abi/parser/abi_types/mod.rs +++ b/starknet-contract/src/abi/parser/abi_types/mod.rs @@ -174,26 +174,19 @@ impl AbiTypeAny { fn parse_type(chars: &mut Peekable) -> Self { let mut generic_types = Vec::new(); let mut current_type = String::new(); - let mut in_generic = false; while let Some(c) = chars.peek() { match c { '<' => { chars.next(); - // In cairo, a generic type is always preceded by a separator "::". + // In cairo, a generic type is always preceeded by a separator "::". let generic_type = Self::parse_generic(current_type.trim_end_matches("::"), chars); generic_types.push(generic_type); - in_generic = true; current_type.clear(); } '>' => { - if in_generic { - chars.next(); - in_generic = false; - } else { - break; - } + break; } '(' => { chars.next(); @@ -247,7 +240,6 @@ impl AbiTypeAny { } ',' => { chars.next(); - inners.push(Self::parse_type(chars)) } _ => { inners.push(Self::parse_type(chars)); @@ -260,8 +252,7 @@ impl AbiTypeAny { } // Array and Span are processed exactly the same, using `Vec`. - let is_array = current_type.contains("core::array::Array") - || current_type.contains("core::array::Span"); + let is_array = current_type.contains("core::array"); if is_array { if inners.len() == 1 { @@ -284,6 +275,9 @@ impl AbiTypeAny { while let Some(c) = chars.peek() { match c { + ' ' => { + chars.next(); + } ',' => { chars.next(); } @@ -292,7 +286,8 @@ impl AbiTypeAny { break; } _ => { - tuple_values.push(Self::parse_type(chars)); + let v = Self::parse_type(chars); + tuple_values.push(v); } } } diff --git a/starknet-contract/src/abi/parser/abi_types/tuple.rs b/starknet-contract/src/abi/parser/abi_types/tuple.rs index 500a8df5..0f03b85b 100644 --- a/starknet-contract/src/abi/parser/abi_types/tuple.rs +++ b/starknet-contract/src/abi/parser/abi_types/tuple.rs @@ -256,4 +256,14 @@ mod tests { ("(core::felt252, B)".to_string(), true) ); } + + #[test] + fn tuple_array_in_array() { + let t = AbiTypeAny::from_string("(core::array::Span::, core::array::Span::>)"); + + assert_eq!( + t.to_rust_type(), + "(Vec, Vec>)" + ); + } } From d4291db25ee9e2c3fbd40e9ec1f9c0b994ab1bfa Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 7 Oct 2023 22:06:11 -0600 Subject: [PATCH 16/31] rename macro variables to avoid conflict with struct/enum members/variants --- starknet-macros/src/abigen/expand/enum.rs | 16 +++++++------- starknet-macros/src/abigen/expand/function.rs | 6 +++-- starknet-macros/src/abigen/expand/struct.rs | 22 +++++++++---------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/starknet-macros/src/abigen/expand/enum.rs b/starknet-macros/src/abigen/expand/enum.rs index 22b221db..85e48860 100644 --- a/starknet-macros/src/abigen/expand/enum.rs +++ b/starknet-macros/src/abigen/expand/enum.rs @@ -88,7 +88,7 @@ impl Expandable for CairoEnum { } }); deserializations.push(quote! { - #i => Ok(#enum_name::#variant_name(#ty_punctuated::deserialize(felts, offset + 1)?)) + #i => Ok(#enum_name::#variant_name(#ty_punctuated::deserialize(__felts, __offset + 1)?)) }); // +1 because we have to handle the variant index also. serialized_sizes.push(quote! { @@ -125,21 +125,21 @@ impl Expandable for CairoEnum { const SERIALIZED_SIZE: std::option::Option = std::option::Option::None; #[inline] - fn serialized_size(rust: &Self::RustType) -> usize { - match rust { + fn serialized_size(__rust: &Self::RustType) -> usize { + match __rust { #(#serialized_sizes),* } } - fn serialize(rust: &Self::RustType) -> Vec { - match rust { + fn serialize(__rust: &Self::RustType) -> Vec { + match __rust { #(#serializations),* } } - fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> starknet::contract::abi::cairo_types::Result { - let index:u128 = felts[offset].try_into().unwrap(); - match index as usize { + fn deserialize(__felts: &[starknet::core::types::FieldElement], __offset: usize) -> starknet::contract::abi::cairo_types::Result { + let __index:u128 = __felts[__offset].try_into().unwrap(); + match __index as usize { #(#deserializations),* } diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 4d8df42f..b41da72f 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -28,7 +28,9 @@ impl Expandable for CairoFunction { let oty = str_to_type(&o.to_rust_type()); quote!(-> starknet::contract::abi::cairo_types::Result<#oty>) } - None => quote!(), + None => { + quote!(-> starknet::contract::abi::cairo_types::Result<()>) + } }, StateMutability::External => { quote!(-> Result quote!(#out_type_path::deserialize(&r, 0)), } } - None => quote!(), + None => quote!(Ok(())), }; match &self.state_mutability { diff --git a/starknet-macros/src/abigen/expand/struct.rs b/starknet-macros/src/abigen/expand/struct.rs index b30fad46..cdcabe64 100644 --- a/starknet-macros/src/abigen/expand/struct.rs +++ b/starknet-macros/src/abigen/expand/struct.rs @@ -68,17 +68,17 @@ impl Expandable for CairoStruct { }; if is_first { - sizes.push(quote!(#ty_punctuated::serialized_size(&rust.#name))); + sizes.push(quote!(#ty_punctuated::serialized_size(&__rust.#name))); is_first = false; } else { - sizes.push(quote!(+ #ty_punctuated::serialized_size(&rust.#name))); + sizes.push(quote!(+ #ty_punctuated::serialized_size(&__rust.#name))); } - sers.push(quote!(out.extend(#ty_punctuated::serialize(&rust.#name));)); + sers.push(quote!(__out.extend(#ty_punctuated::serialize(&__rust.#name));)); desers.push(quote! { - let #name = #ty_punctuated::deserialize(felts, offset)?; - offset += #ty_punctuated::serialized_size(&#name); + let #name = #ty_punctuated::deserialize(__felts, __offset)?; + __offset += #ty_punctuated::serialized_size(&#name); }); } @@ -106,18 +106,18 @@ impl Expandable for CairoStruct { const SERIALIZED_SIZE: std::option::Option = None; #[inline] - fn serialized_size(rust: &Self::RustType) -> usize { + fn serialized_size(__rust: &Self::RustType) -> usize { #(#sizes) * } - fn serialize(rust: &Self::RustType) -> Vec { - let mut out: Vec = vec![]; + fn serialize(__rust: &Self::RustType) -> Vec { + let mut __out: Vec = vec![]; #(#sers)* - out + __out } - fn deserialize(felts: &[starknet::core::types::FieldElement], offset: usize) -> starknet::contract::abi::cairo_types::Result { - let mut offset = offset; + fn deserialize(__felts: &[starknet::core::types::FieldElement], __offset: usize) -> starknet::contract::abi::cairo_types::Result { + let mut __offset = __offset; #(#desers)* Ok(#struct_name { #(#names),* From 85a503421c62100c92c3b0b3ca2871af3fe985fc Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 7 Oct 2023 22:13:42 -0600 Subject: [PATCH 17/31] add bool as enum already managed by built-ins --- starknet-contract/src/abi/cairo_types/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet-contract/src/abi/cairo_types/mod.rs b/starknet-contract/src/abi/cairo_types/mod.rs index 569560ef..b554725d 100644 --- a/starknet-contract/src/abi/cairo_types/mod.rs +++ b/starknet-contract/src/abi/cairo_types/mod.rs @@ -20,7 +20,7 @@ use starknet_core::types::FieldElement; pub const CAIRO_BASIC_STRUCTS: [&str; 4] = ["Span", "ClassHash", "ContractAddress", "EthAddress"]; /// Same as `CAIRO_BASIC_STRUCTS`, but for enums. -pub const CAIRO_BASIC_ENUMS: [&str; 2] = ["Option", "Result"]; +pub const CAIRO_BASIC_ENUMS: [&str; 3] = ["Option", "Result", "bool"]; /// CairoType trait to implement in order to serialize/deserialize /// a Rust type to/from a CairoType. From 60b79ce92c622d0a6fbe23cc93daf7c03dd14a7f Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 7 Oct 2023 22:32:33 -0600 Subject: [PATCH 18/31] make block_id customizable for functionc all --- starknet-macros/src/abigen/expand/contract.rs | 6 ++++++ starknet-macros/src/abigen/expand/function.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index b89378d9..7728716d 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -19,6 +19,7 @@ impl CairoContract { pub address: starknet::core::types::FieldElement, pub provider: std::sync::Arc

, pub account: std::option::Option, starknet::signers::LocalWallet>>>, + call_block_id: starknet::core::types::BlockId, } impl

#contract_name

@@ -33,6 +34,7 @@ impl CairoContract { address, provider: std::sync::Arc::clone(&provider), account: None, + call_block_id: starknet::core::types::BlockId::Tag(starknet::core::types::BlockTag::Pending), } } @@ -41,6 +43,10 @@ impl CairoContract { self.account = Some(std::sync::Arc::clone(&account)); self } + + pub fn set_call_block_id(mut self, block_id: starknet::core::types::BlockId) { + self.call_block_id = block_id; + } } } } diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index b41da72f..6dc994cc 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -96,7 +96,7 @@ impl Expandable for CairoFunction { entry_point_selector: starknet::macros::selector!(#func_name), calldata, }, - BlockId::Tag(BlockTag::Latest), + self.call_block_id, ) .await.map_err( |err| @@ -214,7 +214,7 @@ mod tests { entry_point_selector: starknet::macros::selector!("my_func"), calldata, }, - BlockId::Tag(BlockTag::Latest), + self.call_block_id, ) .await.map_err(|err| starknet::contract::abi::cairo_types::Error::Deserialize(format!("Deserialization error {}" , err)))?; From ca6d5f05e18ff7c24979c9b4518a6415b3f3820d Mon Sep 17 00:00:00 2001 From: glihm Date: Sat, 7 Oct 2023 22:45:48 -0600 Subject: [PATCH 19/31] add thiserror for cairo error --- starknet-contract/src/abi/cairo_types/error.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/starknet-contract/src/abi/cairo_types/error.rs b/starknet-contract/src/abi/cairo_types/error.rs index 7ab1bf8a..b43a0092 100644 --- a/starknet-contract/src/abi/cairo_types/error.rs +++ b/starknet-contract/src/abi/cairo_types/error.rs @@ -6,13 +6,13 @@ use starknet_core::types::FieldElement; pub type Result = core::result::Result; /// A cairo type error. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum Error { - /// Invalid type string. + #[error("Invalid type found {0:?}.")] InvalidTypeString(String), - /// Error during serialization. + #[error("Error during serialization {0:?}.")] Serialize(String), - /// Error during deserialization. + #[error("Error during deserialization {0:?}.")] Deserialize(String), } From 06e1d3ab7c726d4d41c5b8ff2d39256e891ce1a0 Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 12:00:40 -0600 Subject: [PATCH 20/31] rework expansion for contract to use ConnectedAccount --- examples/abigen.rs | 25 ++++++--- examples/abigen_events.rs | 13 +++-- starknet-macros/src/abigen/expand/contract.rs | 53 ++++++++++--------- starknet-macros/src/abigen/expand/function.rs | 22 ++------ starknet-macros/src/abigen/mod.rs | 38 +++++++++---- 5 files changed, 84 insertions(+), 67 deletions(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index d5663e29..e8aeb84c 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -9,8 +9,6 @@ use starknet::{ signers::{LocalWallet, SigningKey}, }; -use std::sync::Arc; - // Generate the bindings for the contract and also includes // all the structs and enums present in the ABI with the exact // same name. @@ -18,19 +16,22 @@ abigen!(TokenContract, "./examples/contracts_abis/mini_erc20.json"); #[tokio::main] async fn main() { - let provider = Arc::new(SequencerGatewayProvider::starknet_alpha_goerli()); + let provider = SequencerGatewayProvider::starknet_alpha_goerli(); + println!("provider {:?}", provider); let eth_goerli_token_address = FieldElement::from_hex_be( "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", ) .unwrap(); - let token_contract = TokenContract::new(eth_goerli_token_address, Arc::clone(&provider)); + // If you only plan to call views functions, you can use the `Reader`, which + // only requires a provider along with your contract address. + let token_contract = TokenContractReader::new(eth_goerli_token_address, &provider); // To call a view, there is no need to initialize an account. You can directly // use the name of the method in the ABI to realize the call. let balance: u256 = token_contract .balanceOf(&ContractAddress( - FieldElement::from_hex_be("YOUR_ACCOUNT_ADDRESS_HEX_HERE").unwrap(), + FieldElement::from_hex_be("YOUR_HEX_CONTRACT_ADDRESS_HERE").unwrap(), )) .await .expect("Call to get balance failed"); @@ -55,7 +56,19 @@ async fn main() { ExecutionEncoding::Legacy, ); - let token_contract = token_contract.with_account(Arc::new(account)); + // The `TokenContract` also contains a reader field that you can use if you need both + // to call external and views with the same instance. + let token_contract = TokenContract::new(eth_goerli_token_address, &account); + + // Example here of querying again the balance, using the internal reader of the + // contract setup with an account. + token_contract + .reader + .balanceOf(&ContractAddress( + FieldElement::from_hex_be("YOUR_HEX_CONTRACT_ADDRESS_HERE").unwrap(), + )) + .await + .expect("Call to get balance failed"); let _ = token_contract .approve( diff --git a/examples/abigen_events.rs b/examples/abigen_events.rs index 6fde356b..65d411f4 100644 --- a/examples/abigen_events.rs +++ b/examples/abigen_events.rs @@ -19,9 +19,13 @@ async fn main() { let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(rpc_url.clone()))); let signer = LocalWallet::from(SigningKey::from_secret_scalar( - FieldElement::from_hex_be("YOUR_PRIVATE_KEY_IN_HEX_HERE").unwrap(), + FieldElement::from_hex_be("0x1800000000300000180000000000030000000000003006001800006600") + .unwrap(), )); - let address = FieldElement::from_hex_be("YOUR_ACCOUNT_CONTRACT_ADDRESS_IN_HEX_HERE").unwrap(); + let address = FieldElement::from_hex_be( + "0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973", + ) + .unwrap(); let account = SingleOwnerAccount::new( provider.clone(), signer, @@ -30,10 +34,9 @@ async fn main() { ExecutionEncoding::Legacy, ); - let contract_address = FieldElement::from_hex_be("CONTRACT_ADDRESS_HEX").unwrap(); + let contract_address = FieldElement::from_hex_be("YOUR_CONTRACT_ADDRESS_HEX").unwrap(); - let event_contract = - Contract::new(contract_address, Arc::clone(&provider)).with_account(Arc::new(account)); + let event_contract = Contract::new(contract_address, &account); // Let emits some events by calling two externals. event_contract diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index 7728716d..9252f23f 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -2,52 +2,53 @@ //! default configuration for provider and account, if any. use proc_macro2::TokenStream as TokenStream2; use quote::quote; - use syn::Ident; +use super::utils; + pub struct CairoContract; impl CairoContract { pub fn expand(contract_name: Ident) -> TokenStream2 { - quote! { + let reader = utils::str_to_ident(format!("{}Reader", contract_name).as_str()); + let q = quote! { #[derive(Debug)] - pub struct #contract_name

- where - P: starknet::providers::Provider + Send + Sync,

::Error: 'static - { + pub struct #contract_name<'a, A: starknet::accounts::ConnectedAccount + Sync> { pub address: starknet::core::types::FieldElement, - pub provider: std::sync::Arc

, - pub account: std::option::Option, starknet::signers::LocalWallet>>>, + pub account: &'a A, + pub reader: #reader<'a, A::Provider>, + } + + impl<'a, A: starknet::accounts::ConnectedAccount + Sync> #contract_name<'a, A> { + pub fn new(address: starknet::core::types::FieldElement, account: &'a A) -> Self { + let reader = #reader::new(address, account.provider()); + Self { address, account, reader } + } + } + + #[derive(Debug)] + pub struct #reader<'a, P: Provider + Sync> { + pub address: starknet::core::types::FieldElement, + pub provider: &'a P, call_block_id: starknet::core::types::BlockId, } - impl

#contract_name

- where - P: starknet::providers::Provider + Send + Sync,

::Error: 'static - { + impl<'a, P: starknet::providers::Provider + Sync> #reader<'a, P> { pub fn new( address: starknet::core::types::FieldElement, - provider: std::sync::Arc

, - ) -> Self { - Self { - address, - provider: std::sync::Arc::clone(&provider), - account: None, - call_block_id: starknet::core::types::BlockId::Tag(starknet::core::types::BlockTag::Pending), - } - } - - pub fn with_account(mut self, account: std::sync::Arc, starknet::signers::LocalWallet>>, + provider: &'a P, ) -> Self { - self.account = Some(std::sync::Arc::clone(&account)); - self + let call_block_id = starknet::core::types::BlockId::Tag(starknet::core::types::BlockTag::Pending); + Self { address, provider, call_block_id } } pub fn set_call_block_id(mut self, block_id: starknet::core::types::BlockId) { self.call_block_id = block_id; } } - } + }; + + q } } diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 6dc994cc..93947b08 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -34,8 +34,7 @@ impl Expandable for CairoFunction { }, StateMutability::External => { quote!(-> Result,

::Error> - > + starknet::accounts::AccountError::Error>> ) } }; @@ -120,15 +119,6 @@ impl Expandable for CairoFunction { use starknet::contract::abi::CairoType; use starknet::accounts::Account; - // TODO: I don't know how to easily store the SingleOwnerAccount - // and it's generic types without complexifiying the whole typing. - // So it's constructed at every call. There is surely a better approach. - let account = match &self.account { - Some(a) => std::sync::Arc::clone(&a), - // TODO: better error handling here. - _ => panic!("Account is required to send invoke transactions") - }; - let mut calldata = vec![]; #(#serializations)* @@ -138,14 +128,8 @@ impl Expandable for CairoFunction { calldata, }]; - let execution = account.execute(calls).fee_estimate_multiplier(2f64); - // TODO: we can have manual fee here, or it can also be estimate only. - let max_fee = execution.estimate_fee().await?.overall_fee.into(); - - execution - .max_fee(max_fee) - .send() - .await + // TODO: add a way for fee estimation and max fee to be parametrizable. + self.account.execute(calls).send().await } }, } diff --git a/starknet-macros/src/abigen/mod.rs b/starknet-macros/src/abigen/mod.rs index 096f532d..3c43c62e 100644 --- a/starknet-macros/src/abigen/mod.rs +++ b/starknet-macros/src/abigen/mod.rs @@ -18,7 +18,7 @@ use std::collections::HashMap; use starknet_contract::abi::cairo_types::{CAIRO_BASIC_ENUMS, CAIRO_BASIC_STRUCTS}; use starknet_contract::abi::parser::{CairoEnum, CairoEvent, CairoFunction, CairoStruct}; -use starknet_core::types::contract::AbiEntry; +use starknet_core::types::contract::{AbiEntry, StateMutability}; mod expand; use expand::contract::CairoContract; @@ -27,6 +27,8 @@ use expand::{Expandable, ExpandableEvent}; mod contract_abi; use contract_abi::ContractAbi; +use crate::abigen::expand::utils; + pub fn abigen_internal(input: TokenStream) -> TokenStream { let contract_abi = parse_macro_input!(input as ContractAbi); let contract_name = contract_abi.name; @@ -38,11 +40,19 @@ pub fn abigen_internal(input: TokenStream) -> TokenStream { let mut structs: HashMap = HashMap::new(); let mut enums: HashMap = HashMap::new(); - let mut functions = vec![]; + let mut views = vec![]; + let mut externals = vec![]; let mut events = vec![]; for entry in &abi { - parse_entry(entry, &mut structs, &mut enums, &mut functions, &mut events); + parse_entry( + entry, + &mut structs, + &mut enums, + &mut externals, + &mut views, + &mut events, + ); } for (_, cs) in structs { @@ -60,12 +70,14 @@ pub fn abigen_internal(input: TokenStream) -> TokenStream { tokens.push(ev.expand_impl(&events)); } + let reader = utils::str_to_ident(format!("{}Reader", contract_name).as_str()); tokens.push(quote! { - impl

#contract_name

- where - P: starknet::providers::Provider + Send + Sync,

::Error: 'static - { - #(#functions)* + impl<'a, A: starknet::accounts::ConnectedAccount + Sync> #contract_name<'a, A> { + #(#externals)* + } + + impl<'a, P: starknet::providers::Provider + Sync> #reader<'a, P> { + #(#views)* } }); @@ -80,7 +92,8 @@ fn parse_entry( entry: &AbiEntry, structs: &mut HashMap, enums: &mut HashMap, - functions: &mut Vec, + externals: &mut Vec, + views: &mut Vec, events: &mut Vec, ) { match entry { @@ -115,7 +128,10 @@ fn parse_entry( // From this statement, we can safely assume that any function name is // unique. let cf = CairoFunction::new(&f.name, f.state_mutability.clone(), &f.inputs, &f.outputs); - functions.push(cf.expand_impl()); + match f.state_mutability { + StateMutability::View => views.push(cf.expand_impl()), + StateMutability::External => externals.push(cf.expand_impl()), + } } AbiEntry::Event(ev) => { if let Some(cev) = CairoEvent::new(ev) { @@ -124,7 +140,7 @@ fn parse_entry( } AbiEntry::Interface(interface) => { for entry in &interface.items { - parse_entry(entry, structs, enums, functions, events); + parse_entry(entry, structs, enums, externals, views, events); } } _ => (), From 2264a157fec146c34dbab45af26a5023fbb8a40d Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 13:41:13 -0600 Subject: [PATCH 21/31] update README.md --- starknet-macros/README.md | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/starknet-macros/README.md b/starknet-macros/README.md index 0ece9da2..c21e7241 100644 --- a/starknet-macros/README.md +++ b/starknet-macros/README.md @@ -1 +1,85 @@ # Procedural macros for `starknet` + +## abigen + +The `abigen` macro aims at generating rust binding from an `ABI` of a smart contract. +The generated bindings contains all the functions, events, structs and enums that are +present in the `ABI` file. + +Some types are directly mapped to rust native types (like integers, `Result`, `Option`, boolean etc..), +other specific types like `ContractAddress`, `ClassHash` or `EthAddress` are managed by `starknet-rs`, +and all other types found in the `ABI` are generated as `struct` or `enum` as necessary. + +`abigen` will generate all the serialization/deserialization code that is required to +work with plain rust types. + +For instance: +```rust +// Cairo function like fn view_1(self: @ContractState, v: felt252, s: Span) +// is generated in rust like: + +fn view_1(v: FieldElement, s: Vec); +``` + +To generate the bindings for your contract, you can do the following: +```rust +use starknet::macros::abigen; + +abigen!(MyContract, "/path/to/abi.json"); +``` +This will generate all the types and two `struct` for the contract: + +1. `MyContractReader`, which is use to call `view` functions that are only reading the blockchain state. + To initialize a reader, you need your contract address and a provider: + ```rust + let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); + let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); + let contract_address = FieldElement::from_hex_be("0x123...").unwrap(); + + let reader = MyContractReader::new(contract_address, &provider); + let result = reader.my_view_1().await; + ``` +2. `MyContract`, which in turn is used to call `external` functions, where a transaction is actually sent to the blockchain. + This one requires an account, to sign those transactions: + ```rust + let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); + let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); + + let signer = LocalWallet::from(SigningKey::from_secret_scalar( + FieldElement::from_hex_be("").unwrap(), + )); + + let account_address = FieldElement::from_hex_be("").unwrap(); + let account = SingleOwnerAccount::new( + provider.clone(), + signer, + address, + felt!("0x4b4154414e41"), // KATANA + ExecutionEncoding::Legacy, + ); + + let contract_address = FieldElement::from_hex_be("0x123...").unwrap(); + + let reader = MyContract::new(contract_address, &account); + let result = reader.my_external_1().await; + ``` + +An other feature provided by `abigen` macro is the capabilities of deserialiazing events. +In the `ABI`, there is always an `Event` enum, which contains all the events declared in your contract. + +You can then do the following: +```rust +let even_page = provider.fetch_events(...); +for e in event_page.events { + let my_event: Event = match e.try_into() { + Ok(ev) => ev, + Err(_) => continue; // This is an event from an other contract or you may use an out-dated ABI. + }; + + match my_event { + Event::MyEventA(a) => // work with a, already typed and deserialized, + Event::MyEventB(b) => // work with b, already typed and deserialized, + ... + }; +} +``` From e32d98a5c9894bc0b3a1831f57615ff25c456bb0 Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 13:44:16 -0600 Subject: [PATCH 22/31] run prettier --- starknet-macros/README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/starknet-macros/README.md b/starknet-macros/README.md index c21e7241..f8d854fc 100644 --- a/starknet-macros/README.md +++ b/starknet-macros/README.md @@ -14,23 +14,27 @@ and all other types found in the `ABI` are generated as `struct` or `enum` as ne work with plain rust types. For instance: + ```rust // Cairo function like fn view_1(self: @ContractState, v: felt252, s: Span) // is generated in rust like: -fn view_1(v: FieldElement, s: Vec); +fn view_1(v: FieldElement, s: Vec); ``` To generate the bindings for your contract, you can do the following: + ```rust use starknet::macros::abigen; abigen!(MyContract, "/path/to/abi.json"); ``` + This will generate all the types and two `struct` for the contract: 1. `MyContractReader`, which is use to call `view` functions that are only reading the blockchain state. To initialize a reader, you need your contract address and a provider: + ```rust let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); @@ -39,12 +43,14 @@ This will generate all the types and two `struct` for the contract: let reader = MyContractReader::new(contract_address, &provider); let result = reader.my_view_1().await; ``` + 2. `MyContract`, which in turn is used to call `external` functions, where a transaction is actually sent to the blockchain. This one requires an account, to sign those transactions: + ```rust let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); - + let signer = LocalWallet::from(SigningKey::from_secret_scalar( FieldElement::from_hex_be("").unwrap(), )); @@ -54,7 +60,7 @@ This will generate all the types and two `struct` for the contract: provider.clone(), signer, address, - felt!("0x4b4154414e41"), // KATANA + felt!("0x4b4154414e41"), // KATANA ExecutionEncoding::Legacy, ); @@ -68,6 +74,7 @@ An other feature provided by `abigen` macro is the capabilities of deserialiazin In the `ABI`, there is always an `Event` enum, which contains all the events declared in your contract. You can then do the following: + ```rust let even_page = provider.fetch_events(...); for e in event_page.events { From 272310d2fce5006659762ddda2f43578be3279ab Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 18:45:19 -0600 Subject: [PATCH 23/31] add missing path for Prodiver --- starknet-macros/src/abigen/expand/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index 9252f23f..f844f7e8 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -28,7 +28,7 @@ impl CairoContract { } #[derive(Debug)] - pub struct #reader<'a, P: Provider + Sync> { + pub struct #reader<'a, P: starknet::providers::Provider + Sync> { pub address: starknet::core::types::FieldElement, pub provider: &'a P, call_block_id: starknet::core::types::BlockId, From e735878639ee514711bbc7a25964c82a12c47f01 Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 18:45:58 -0600 Subject: [PATCH 24/31] add convertion for basic starknet type to FieldElement --- examples/abigen.rs | 2 +- .../src/abi/cairo_types/types/starknet.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index e8aeb84c..7506722f 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -5,7 +5,7 @@ use starknet::{ contract::abi::ContractAddress, core::{chain_id, types::FieldElement}, macros::abigen, - providers::{Provider, SequencerGatewayProvider}, + providers::SequencerGatewayProvider, signers::{LocalWallet, SigningKey}, }; diff --git a/starknet-contract/src/abi/cairo_types/types/starknet.rs b/starknet-contract/src/abi/cairo_types/types/starknet.rs index f191f682..6ce7aff4 100644 --- a/starknet-contract/src/abi/cairo_types/types/starknet.rs +++ b/starknet-contract/src/abi/cairo_types/types/starknet.rs @@ -14,6 +14,12 @@ impl From for ContractAddress { } } +impl From for FieldElement { + fn from(item: ContractAddress) -> Self { + item.0 + } +} + impl CairoType for ContractAddress { type RustType = Self; @@ -36,6 +42,12 @@ impl From for ClassHash { } } +impl From for FieldElement { + fn from(item: ClassHash) -> Self { + item.0 + } +} + impl CairoType for ClassHash { type RustType = Self; @@ -58,6 +70,12 @@ impl From for EthAddress { } } +impl From for FieldElement { + fn from(item: EthAddress) -> Self { + item.0 + } +} + impl CairoType for EthAddress { type RustType = Self; From 339348f651928d7d72c3e1e1e8fe8350179a5cd4 Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 18:58:37 -0600 Subject: [PATCH 25/31] derive more attribute for starknet base types --- starknet-contract/src/abi/cairo_types/types/starknet.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/starknet-contract/src/abi/cairo_types/types/starknet.rs b/starknet-contract/src/abi/cairo_types/types/starknet.rs index 6ce7aff4..9f5c5580 100644 --- a/starknet-contract/src/abi/cairo_types/types/starknet.rs +++ b/starknet-contract/src/abi/cairo_types/types/starknet.rs @@ -5,7 +5,7 @@ use crate::abi::cairo_types::{CairoType, Result}; use starknet_core::types::FieldElement; /// ContractAddress. -#[derive(Debug, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct ContractAddress(pub FieldElement); impl From for ContractAddress { @@ -33,7 +33,7 @@ impl CairoType for ContractAddress { } /// ClassHash. -#[derive(Debug, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct ClassHash(pub FieldElement); impl From for ClassHash { @@ -61,7 +61,7 @@ impl CairoType for ClassHash { } /// EthAddress. -#[derive(Debug, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct EthAddress(pub FieldElement); impl From for EthAddress { From d9c2e112eaa97e0f1116b9168dede5e38013ee9b Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 18:59:11 -0600 Subject: [PATCH 26/31] rename calldata in macro to avoid naming conflicts --- starknet-macros/src/abigen/expand/function.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 93947b08..c6e67a05 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -58,9 +58,9 @@ impl Expandable for CairoFunction { let ser = match abi_type { AbiTypeAny::Tuple(_) => quote! { - calldata.extend(<#ty>::serialize(#name)); + __calldata.extend(<#ty>::serialize(#name)); }, - _ => quote!(calldata.extend(#ty::serialize(#name));), + _ => quote!(__calldata.extend(#ty::serialize(#name));), }; serializations.push(ser); } @@ -85,7 +85,7 @@ impl Expandable for CairoFunction { use starknet::contract::abi::CairoType; use starknet::core::types::{BlockId, BlockTag}; - let mut calldata = vec![]; + let mut __calldata = vec![]; #(#serializations)* let r = self.provider @@ -93,7 +93,7 @@ impl Expandable for CairoFunction { starknet::core::types::FunctionCall { contract_address: self.address, entry_point_selector: starknet::macros::selector!(#func_name), - calldata, + calldata: __calldata, }, self.call_block_id, ) @@ -119,13 +119,13 @@ impl Expandable for CairoFunction { use starknet::contract::abi::CairoType; use starknet::accounts::Account; - let mut calldata = vec![]; + let mut __calldata = vec![]; #(#serializations)* let calls = vec![starknet::accounts::Call { to: self.address, selector: starknet::macros::selector!(#func_name), - calldata, + calldata: __calldata, }]; // TODO: add a way for fee estimation and max fee to be parametrizable. @@ -187,16 +187,16 @@ mod tests { use starknet::contract::abi::CairoType; use starknet::core::types::{BlockId, BlockTag}; - let mut calldata = vec![]; - calldata.extend(starknet::core::types::FieldElement::serialize(v1)); - calldata.extend(starknet::core::types::FieldElement::serialize(v2)); + let mut __calldata = vec![]; + __calldata.extend(starknet::core::types::FieldElement::serialize(v1)); + __calldata.extend(starknet::core::types::FieldElement::serialize(v2)); let r = self.provider .call( starknet::core::types::FunctionCall { contract_address: self.address, entry_point_selector: starknet::macros::selector!("my_func"), - calldata, + __calldata, }, self.call_block_id, ) From a190eddcc1490c96723c43121afcd3a3c0a6d340 Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 20:03:57 -0600 Subject: [PATCH 27/31] add getter for call_block_id --- starknet-macros/src/abigen/expand/contract.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index f844f7e8..445e7c3f 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -46,6 +46,10 @@ impl CairoContract { pub fn set_call_block_id(mut self, block_id: starknet::core::types::BlockId) { self.call_block_id = block_id; } + + pub fn get_call_block_id(&self) -> starknet::core::types::BlockId { + self.call_block_id + } } }; From e35c40d9906f718bdb99a3879241dcd64e2b6dbf Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 20:09:28 -0600 Subject: [PATCH 28/31] fix tests --- starknet-macros/README.md | 10 +++++----- starknet-macros/src/abigen/expand/function.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/starknet-macros/README.md b/starknet-macros/README.md index f8d854fc..7bb0379f 100644 --- a/starknet-macros/README.md +++ b/starknet-macros/README.md @@ -15,7 +15,7 @@ work with plain rust types. For instance: -```rust +```rust,ignore // Cairo function like fn view_1(self: @ContractState, v: felt252, s: Span) // is generated in rust like: @@ -24,7 +24,7 @@ fn view_1(v: FieldElement, s: Vec); To generate the bindings for your contract, you can do the following: -```rust +```rust,ignore use starknet::macros::abigen; abigen!(MyContract, "/path/to/abi.json"); @@ -35,7 +35,7 @@ This will generate all the types and two `struct` for the contract: 1. `MyContractReader`, which is use to call `view` functions that are only reading the blockchain state. To initialize a reader, you need your contract address and a provider: - ```rust + ```rust,ignore let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); let contract_address = FieldElement::from_hex_be("0x123...").unwrap(); @@ -47,7 +47,7 @@ This will generate all the types and two `struct` for the contract: 2. `MyContract`, which in turn is used to call `external` functions, where a transaction is actually sent to the blockchain. This one requires an account, to sign those transactions: - ```rust + ```rust,ignore let rpc_url = Url::parse("http://0.0.0.0:5050").unwrap(); let provider = JsonRpcClient::new(HttpTransport::new(rpc_url.clone())); @@ -75,7 +75,7 @@ In the `ABI`, there is always an `Event` enum, which contains all the events dec You can then do the following: -```rust +```rust,ignore let even_page = provider.fetch_events(...); for e in event_page.events { let my_event: Event = match e.try_into() { diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index c6e67a05..2241a1ee 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -196,7 +196,7 @@ mod tests { starknet::core::types::FunctionCall { contract_address: self.address, entry_point_selector: starknet::macros::selector!("my_func"), - __calldata, + calldata: __calldata, }, self.call_block_id, ) From bfbcb5e22b471b58b191036e7441d3c5dfd5b379 Mon Sep 17 00:00:00 2001 From: glihm Date: Tue, 10 Oct 2023 20:52:05 -0600 Subject: [PATCH 29/31] take mut ref instead of ownership to change call_block_id --- starknet-macros/src/abigen/expand/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index 445e7c3f..c5aae7dc 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -43,7 +43,7 @@ impl CairoContract { Self { address, provider, call_block_id } } - pub fn set_call_block_id(mut self, block_id: starknet::core::types::BlockId) { + pub fn set_call_block_id(&mut self, block_id: starknet::core::types::BlockId) { self.call_block_id = block_id; } From 1a39dea15960d3d10250845c478b3d3d217ba357 Mon Sep 17 00:00:00 2001 From: glihm Date: Fri, 10 Nov 2023 09:33:31 -0600 Subject: [PATCH 30/31] fix: autogen contract takes now ConntectAccount instead of a reference --- examples/abigen.rs | 2 +- starknet-macros/src/abigen/expand/contract.rs | 16 +++++++++------- starknet-macros/src/abigen/mod.rs | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/abigen.rs b/examples/abigen.rs index 7506722f..1135b32f 100644 --- a/examples/abigen.rs +++ b/examples/abigen.rs @@ -63,7 +63,7 @@ async fn main() { // Example here of querying again the balance, using the internal reader of the // contract setup with an account. token_contract - .reader + .reader() .balanceOf(&ContractAddress( FieldElement::from_hex_be("YOUR_HEX_CONTRACT_ADDRESS_HERE").unwrap(), )) diff --git a/starknet-macros/src/abigen/expand/contract.rs b/starknet-macros/src/abigen/expand/contract.rs index c5aae7dc..8774d103 100644 --- a/starknet-macros/src/abigen/expand/contract.rs +++ b/starknet-macros/src/abigen/expand/contract.rs @@ -14,16 +14,18 @@ impl CairoContract { let q = quote! { #[derive(Debug)] - pub struct #contract_name<'a, A: starknet::accounts::ConnectedAccount + Sync> { + pub struct #contract_name { pub address: starknet::core::types::FieldElement, - pub account: &'a A, - pub reader: #reader<'a, A::Provider>, + pub account: A, } - impl<'a, A: starknet::accounts::ConnectedAccount + Sync> #contract_name<'a, A> { - pub fn new(address: starknet::core::types::FieldElement, account: &'a A) -> Self { - let reader = #reader::new(address, account.provider()); - Self { address, account, reader } + impl #contract_name { + pub fn new(address: starknet::core::types::FieldElement, account: A) -> Self { + Self { address, account } + } + + pub fn reader(&self) -> #reader { + #reader::new(self.address, self.account.provider()) } } diff --git a/starknet-macros/src/abigen/mod.rs b/starknet-macros/src/abigen/mod.rs index 3c43c62e..ba3f1974 100644 --- a/starknet-macros/src/abigen/mod.rs +++ b/starknet-macros/src/abigen/mod.rs @@ -72,7 +72,7 @@ pub fn abigen_internal(input: TokenStream) -> TokenStream { let reader = utils::str_to_ident(format!("{}Reader", contract_name).as_str()); tokens.push(quote! { - impl<'a, A: starknet::accounts::ConnectedAccount + Sync> #contract_name<'a, A> { + impl #contract_name { #(#externals)* } From a28d1b9ed04b7ba38465771ac8f4aed3844babc8 Mon Sep 17 00:00:00 2001 From: glihm Date: Fri, 10 Nov 2023 09:38:19 -0600 Subject: [PATCH 31/31] fix: adjust to provider change in 0.7.0 --- starknet-macros/src/abigen/expand/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet-macros/src/abigen/expand/function.rs b/starknet-macros/src/abigen/expand/function.rs index 2241a1ee..536a0440 100644 --- a/starknet-macros/src/abigen/expand/function.rs +++ b/starknet-macros/src/abigen/expand/function.rs @@ -34,7 +34,7 @@ impl Expandable for CairoFunction { }, StateMutability::External => { quote!(-> Result::Error>> + starknet::accounts::AccountError> ) } };