From 19389b8f2b6388146f48b174ac7fbef8c04a39a6 Mon Sep 17 00:00:00 2001 From: Oche Date: Tue, 16 Jul 2024 16:58:25 +0100 Subject: [PATCH] feat: add support for signed integers (#2161) * feat: add support for signed integers * chore: run scripts/rust_fmt.sh * feat: add support for signed integers * chore: run scripts/rust_fmt.sh * feat: decode signed integer * test: test signed integers decoder * refac: remove redundant type casting * refac: modify signed integer decoder * feat: implement try from felt for signed integers * Delete scripts/prettier.sh * chore: add comments * fix: ensure signed integers are correctly printed * fix: add more tests --------- Co-authored-by: glihm --- bin/sozo/src/commands/calldata_decoder.rs | 66 +++++++ bin/sozo/src/commands/execute.rs | 1 + .../dojo-core/src/database/introspect.cairo | 60 ++++++ crates/dojo-types/src/lib.rs | 1 + crates/dojo-types/src/primitive.rs | 177 ++++++++++++++--- crates/dojo-types/src/primitive_conversion.rs | 105 ++++++++++ crates/dojo-types/src/schema.rs | 179 +++++++++++++++++- crates/sozo/ops/src/model.rs | 5 + crates/torii/core/src/model.rs | 38 +++- crates/torii/grpc/src/types/schema.rs | 11 +- crates/torii/types-test/src/contracts.cairo | 5 + crates/torii/types-test/src/models.cairo | 7 +- .../dojo_examples-PlayerConfig-3adad785.json | 4 + .../dojo_examples-actions-40b6994c.toml | 4 +- .../dojo_examples-PlayerConfig-3adad785.toml | 4 +- .../dojo_examples-PlayerConfig-3adad785.json | 4 + .../manifests/dev/deployment/manifest.json | 12 +- .../manifests/dev/deployment/manifest.toml | 8 +- .../dojo_examples-PlayerConfig-3adad785.json | 4 + .../dojo_examples-actions-40b6994c.toml | 4 +- .../dojo_examples-PlayerConfig-3adad785.toml | 4 +- examples/spawn-and-move/src/actions.cairo | 3 +- examples/spawn-and-move/src/models.cairo | 1 + scripts/prettier.sh | 6 - 24 files changed, 657 insertions(+), 56 deletions(-) create mode 100644 crates/dojo-types/src/primitive_conversion.rs delete mode 100644 scripts/prettier.sh diff --git a/bin/sozo/src/commands/calldata_decoder.rs b/bin/sozo/src/commands/calldata_decoder.rs index 082f50d7cd..fd74e0aa46 100644 --- a/bin/sozo/src/commands/calldata_decoder.rs +++ b/bin/sozo/src/commands/calldata_decoder.rs @@ -93,6 +93,18 @@ impl CalldataDecoder for ShortStrCalldataDecoder { } } +/// Decodes a signed integer into a [`Felt`] +struct SignedIntegerCalldataDecoder; +impl CalldataDecoder for SignedIntegerCalldataDecoder { + fn decode(&self, input: &str) -> DecoderResult> { + if let Ok(value) = input.parse::() { + Ok(vec![value.into()]) + } else { + Err(CalldataDecoderError::ParseError("Invalid numeric string".to_string())) + } + } +} + /// Decodes a string into a [`Felt`], either from hexadecimal or decimal string. struct DefaultCalldataDecoder; impl CalldataDecoder for DefaultCalldataDecoder { @@ -150,6 +162,7 @@ fn decode_inner(item: &str) -> DecoderResult> { "u256" => U256CalldataDecoder.decode(value)?, "str" => StrCalldataDecoder.decode(value)?, "sstr" => ShortStrCalldataDecoder.decode(value)?, + "int" => SignedIntegerCalldataDecoder.decode(value)?, _ => DefaultCalldataDecoder.decode(item)?, } } else { @@ -236,6 +249,51 @@ mod tests { assert_eq!(result, expected); } + #[test] + fn test_signed_integer_decoder_i8() { + let input = "-64"; + let signed_i8: i8 = -64; + let expected = vec![signed_i8.into()]; + let result = decode_calldata(input).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn test_signed_integer_decoder_i16() { + let input = "-12345"; + let signed_i16: i16 = -12345; + let expected = vec![signed_i16.into()]; + let result = decode_calldata(input).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn test_signed_integer_decoder_i32() { + let input = "-987654321"; + let signed_i32: i32 = -987654321; + let expected = vec![signed_i32.into()]; + let result = decode_calldata(input).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn test_signed_integer_decoder_i64() { + let input = "-1234567890123456789"; + let signed_i64: i64 = -1234567890123456789; + let expected = vec![signed_i64.into()]; + let result = decode_calldata(input).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn test_signed_integer_decoder_i128() { + let input = "-123456789012345678901234567890123456"; + let signed_i128: i128 = -123456789012345678901234567890123456; + let expected = vec![signed_i128.into()]; + let result = decode_calldata(input).unwrap(); + assert_eq!(result, expected); + } + #[test] fn test_combined_decoders() { let input = "u256:0x64,str:world,987654,0x123"; @@ -259,4 +317,12 @@ mod tests { let result = decode_calldata(input).unwrap(); assert_eq!(result, expected); } + + #[test] + fn test_invalid_signed_integer_decoder() { + let input = "-12345abc"; + let decoder = SignedIntegerCalldataDecoder; + let result = decoder.decode(input); + assert!(result.is_err()); + } } diff --git a/bin/sozo/src/commands/execute.rs b/bin/sozo/src/commands/execute.rs index 9d2dcf4faa..145b0b81e0 100644 --- a/bin/sozo/src/commands/execute.rs +++ b/bin/sozo/src/commands/execute.rs @@ -31,6 +31,7 @@ pub struct ExecuteArgs { - u256: A 256-bit unsigned integer. - sstr: A cairo short string. - str: A cairo string (ByteArray). + - int: A signed integer. - no prefix: A cairo felt or any type that fit into one felt.")] pub calldata: Option, diff --git a/crates/dojo-core/src/database/introspect.cairo b/crates/dojo-core/src/database/introspect.cairo index 1eb1e2a1ad..8cd2332b17 100644 --- a/crates/dojo-core/src/database/introspect.cairo +++ b/crates/dojo-core/src/database/introspect.cairo @@ -156,6 +156,66 @@ impl Introspect_u256 of Introspect { } } +impl Introspect_i8 of Introspect { + fn size() -> Option { + Option::Some(1) + } + fn layout() -> Layout { + Layout::Fixed(array![251].span()) + } + fn ty() -> Ty { + Ty::Primitive('i8') + } +} + +impl Introspect_i16 of Introspect { + fn size() -> Option { + Option::Some(1) + } + fn layout() -> Layout { + Layout::Fixed(array![251].span()) + } + fn ty() -> Ty { + Ty::Primitive('i16') + } +} + +impl Introspect_i32 of Introspect { + fn size() -> Option { + Option::Some(1) + } + fn layout() -> Layout { + Layout::Fixed(array![251].span()) + } + fn ty() -> Ty { + Ty::Primitive('i32') + } +} + +impl Introspect_i64 of Introspect { + fn size() -> Option { + Option::Some(1) + } + fn layout() -> Layout { + Layout::Fixed(array![251].span()) + } + fn ty() -> Ty { + Ty::Primitive('i64') + } +} + +impl Introspect_i128 of Introspect { + fn size() -> Option { + Option::Some(1) + } + fn layout() -> Layout { + Layout::Fixed(array![251].span()) + } + fn ty() -> Ty { + Ty::Primitive('i128') + } +} + impl Introspect_address of Introspect { fn size() -> Option { Option::Some(1) diff --git a/crates/dojo-types/src/lib.rs b/crates/dojo-types/src/lib.rs index 3ced1a5883..4a3636f980 100644 --- a/crates/dojo-types/src/lib.rs +++ b/crates/dojo-types/src/lib.rs @@ -7,6 +7,7 @@ use starknet::core::types::Felt; pub mod event; pub mod packing; pub mod primitive; +pub mod primitive_conversion; pub mod schema; pub mod storage; pub mod system; diff --git a/crates/dojo-types/src/primitive.rs b/crates/dojo-types/src/primitive.rs index 6192c107e3..334a4728b0 100644 --- a/crates/dojo-types/src/primitive.rs +++ b/crates/dojo-types/src/primitive.rs @@ -7,6 +7,8 @@ use starknet::core::types::Felt; use strum::IntoEnumIterator; use strum_macros::{AsRefStr, Display, EnumIter, EnumString}; +use super::primitive_conversion::try_from_felt; + #[derive( AsRefStr, Display, @@ -25,6 +27,11 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumString}; #[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] pub enum Primitive { + I8(Option), + I16(Option), + I32(Option), + I64(Option), + I128(Option), U8(Option), U16(Option), U32(Option), @@ -56,6 +63,8 @@ pub enum PrimitiveError { CairoSerde(#[from] cainome::cairo_serde::Error), #[error(transparent)] FromUtf8Error(#[from] std::string::FromUtf8Error), + #[error(transparent)] + FeltFromFeltError(#[from] crate::primitive_conversion::PrimitiveFromFeltError), } #[derive(AsRefStr, Debug, Display, EnumString, PartialEq)] @@ -96,26 +105,36 @@ macro_rules! as_primitive { } impl Primitive { + as_primitive!(as_i8, I8, i8); + as_primitive!(as_i16, I16, i16); + as_primitive!(as_i32, I32, i32); + as_primitive!(as_i64, I64, i64); + as_primitive!(as_i128, I128, i128); as_primitive!(as_u8, U8, u8); as_primitive!(as_u16, U16, u16); as_primitive!(as_u32, U32, u32); as_primitive!(as_u64, U64, u64); as_primitive!(as_u128, U128, u128); as_primitive!(as_u256, U256, U256); - as_primitive!(as_bool, Bool, bool); as_primitive!(as_usize, USize, u32); + as_primitive!(as_bool, Bool, bool); as_primitive!(as_felt252, Felt252, Felt); as_primitive!(as_class_hash, ClassHash, Felt); as_primitive!(as_contract_address, ContractAddress, Felt); + set_primitive!(set_i8, I8, i8); + set_primitive!(set_i16, I16, i16); + set_primitive!(set_i32, I32, i32); + set_primitive!(set_i64, I64, i64); + set_primitive!(set_i128, I128, i128); set_primitive!(set_u8, U8, u8); set_primitive!(set_u16, U16, u16); set_primitive!(set_u32, U32, u32); set_primitive!(set_u64, U64, u64); set_primitive!(set_u128, U128, u128); set_primitive!(set_u256, U256, U256); - set_primitive!(set_bool, Bool, bool); set_primitive!(set_usize, USize, u32); + set_primitive!(set_bool, Bool, bool); set_primitive!(set_felt252, Felt252, Felt); set_primitive!(set_class_hash, ClassHash, Felt); set_primitive!(set_contract_address, ContractAddress, Felt); @@ -133,6 +152,11 @@ impl Primitive { Primitive::Felt252(_) => 8, Primitive::ClassHash(_) => 9, Primitive::ContractAddress(_) => 10, + Primitive::I8(_) => 11, + Primitive::I16(_) => 12, + Primitive::I32(_) => 13, + Primitive::I64(_) => 14, + Primitive::I128(_) => 15, } } @@ -142,7 +166,12 @@ impl Primitive { pub fn to_sql_type(&self) -> SqlType { match self { - Primitive::U8(_) + Primitive::I8(_) + | Primitive::I16(_) + | Primitive::I32(_) + | Primitive::I64(_) + | Primitive::I128(_) + | Primitive::U8(_) | Primitive::U16(_) | Primitive::U32(_) | Primitive::USize(_) @@ -165,6 +194,12 @@ impl Primitive { } match self { + Primitive::I8(_) => Ok(format!("{}", try_from_felt::(value[0])?)), + Primitive::I16(_) => Ok(format!("{}", try_from_felt::(value[0])?)), + Primitive::I32(_) => Ok(format!("{}", try_from_felt::(value[0])?)), + Primitive::I64(_) => Ok(format!("{}", try_from_felt::(value[0])?)), + Primitive::I128(_) => Ok(format!("{}", try_from_felt::(value[0])?)), + Primitive::U8(_) | Primitive::U16(_) | Primitive::U32(_) @@ -198,6 +233,41 @@ impl Primitive { } match self { + Primitive::I8(ref mut value) => { + let felt = felts.remove(0); + *value = Some(try_from_felt::(felt).map_err(|_| { + PrimitiveError::ValueOutOfRange { r#type: type_name::(), value: felt } + })?); + } + + Primitive::I16(ref mut value) => { + let felt = felts.remove(0); + *value = Some(try_from_felt::(felt).map_err(|_| { + PrimitiveError::ValueOutOfRange { r#type: type_name::(), value: felt } + })?); + } + + Primitive::I32(ref mut value) => { + let felt = felts.remove(0); + *value = Some(try_from_felt::(felt).map_err(|_| { + PrimitiveError::ValueOutOfRange { r#type: type_name::(), value: felt } + })?); + } + + Primitive::I64(ref mut value) => { + let felt = felts.remove(0); + *value = Some(try_from_felt::(felt).map_err(|_| { + PrimitiveError::ValueOutOfRange { r#type: type_name::(), value: felt } + })?); + } + + Primitive::I128(ref mut value) => { + let felt = felts.remove(0); + *value = Some(try_from_felt::(felt).map_err(|_| { + PrimitiveError::ValueOutOfRange { r#type: type_name::(), value: felt } + })?); + } + Primitive::U8(ref mut value) => { let felt = felts.remove(0); *value = Some(felt.to_u8().ok_or_else(|| PrimitiveError::ValueOutOfRange { @@ -230,19 +300,6 @@ impl Primitive { })?); } - Primitive::USize(ref mut value) => { - let felt = felts.remove(0); - *value = Some(felt.to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange { - r#type: type_name::(), - value: felt, - })?); - } - - Primitive::Bool(ref mut value) => { - let raw = felts.remove(0); - *value = Some(raw == Felt::ONE); - } - Primitive::U128(ref mut value) => { let felt = felts.remove(0); *value = Some(felt.to_u128().ok_or_else(|| PrimitiveError::ValueOutOfRange { @@ -265,6 +322,19 @@ impl Primitive { *value = Some(U256::from_be_bytes(bytes)); } + Primitive::USize(ref mut value) => { + let felt = felts.remove(0); + *value = Some(felt.to_u32().ok_or_else(|| PrimitiveError::ValueOutOfRange { + r#type: type_name::(), + value: felt, + })?); + } + + Primitive::Bool(ref mut value) => { + let raw = felts.remove(0); + *value = Some(raw == Felt::ONE); + } + Primitive::ContractAddress(ref mut value) => { *value = Some(felts.remove(0)); } @@ -283,6 +353,21 @@ impl Primitive { pub fn serialize(&self) -> Result, PrimitiveError> { match self { + Primitive::I8(value) => value + .map(|v| Ok(vec![Felt::from(v)])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), + Primitive::I16(value) => value + .map(|v| Ok(vec![Felt::from(v)])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), + Primitive::I32(value) => value + .map(|v| Ok(vec![Felt::from(v)])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), + Primitive::I64(value) => value + .map(|v| Ok(vec![Felt::from(v)])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), + Primitive::I128(value) => value + .map(|v| Ok(vec![Felt::from(v)])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), Primitive::U8(value) => value .map(|v| Ok(vec![Felt::from(v)])) .unwrap_or(Err(PrimitiveError::MissingFieldElement)), @@ -295,12 +380,6 @@ impl Primitive { Primitive::U64(value) => value .map(|v| Ok(vec![Felt::from(v)])) .unwrap_or(Err(PrimitiveError::MissingFieldElement)), - Primitive::USize(value) => value - .map(|v| Ok(vec![Felt::from(v)])) - .unwrap_or(Err(PrimitiveError::MissingFieldElement)), - Primitive::Bool(value) => value - .map(|v| Ok(vec![if v { Felt::ONE } else { Felt::ZERO }])) - .unwrap_or(Err(PrimitiveError::MissingFieldElement)), Primitive::U128(value) => value .map(|v| Ok(vec![Felt::from(v)])) .unwrap_or(Err(PrimitiveError::MissingFieldElement)), @@ -318,6 +397,12 @@ impl Primitive { Ok(vec![value0, value1]) }) .unwrap_or(Err(PrimitiveError::MissingFieldElement)), + Primitive::USize(value) => value + .map(|v| Ok(vec![Felt::from(v)])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), + Primitive::Bool(value) => value + .map(|v| Ok(vec![if v { Felt::ONE } else { Felt::ZERO }])) + .unwrap_or(Err(PrimitiveError::MissingFieldElement)), Primitive::ContractAddress(value) => { value.map(|v| Ok(vec![v])).unwrap_or(Err(PrimitiveError::MissingFieldElement)) } @@ -364,6 +449,21 @@ mod tests { #[test] fn inner_value_getter_setter() { + let mut primitive = Primitive::I8(None); + primitive.set_i8(Some(-1i8)).unwrap(); + assert_eq!(primitive.as_i8(), Some(-1i8)); + let mut primitive = Primitive::I16(None); + primitive.set_i16(Some(-1i16)).unwrap(); + assert_eq!(primitive.as_i16(), Some(-1i16)); + let mut primitive = Primitive::I32(None); + primitive.set_i32(Some(-1i32)).unwrap(); + assert_eq!(primitive.as_i32(), Some(-1i32)); + let mut primitive = Primitive::I64(None); + primitive.set_i64(Some(-1i64)).unwrap(); + assert_eq!(primitive.as_i64(), Some(-1i64)); + let mut primitive = Primitive::I128(None); + primitive.set_i128(Some(-1i128)).unwrap(); + assert_eq!(primitive.as_i128(), Some(-1i128)); let mut primitive = Primitive::U8(None); primitive.set_u8(Some(1u8)).unwrap(); assert_eq!(primitive.as_u8(), Some(1u8)); @@ -398,4 +498,37 @@ mod tests { primitive.set_contract_address(Some(Felt::from(1u128))).unwrap(); assert_eq!(primitive.as_contract_address(), Some(Felt::from(1u128))); } + + #[test] + fn test_primitive_deserialization() { + let test_cases = vec![ + (vec![Felt::from(-42i8)], Primitive::I8(Some(-42))), + (vec![Felt::from(-1000i16)], Primitive::I16(Some(-1000))), + (vec![Felt::from(-100000i32)], Primitive::I32(Some(-100000))), + (vec![Felt::from(-1000000000i64)], Primitive::I64(Some(-1000000000))), + ( + vec![Felt::from(-1000000000000000000i128)], + Primitive::I128(Some(-1000000000000000000)), + ), + (vec![Felt::from(42u8)], Primitive::U8(Some(42))), + (vec![Felt::from(1000u16)], Primitive::U16(Some(1000))), + (vec![Felt::from(100000u32)], Primitive::U32(Some(100000))), + (vec![Felt::from(1000000000u64)], Primitive::U64(Some(1000000000))), + (vec![Felt::from(1000000000000000000u128)], Primitive::U128(Some(1000000000000000000))), + (vec![Felt::from(42u32)], Primitive::USize(Some(42))), + (vec![Felt::from(1u8)], Primitive::Bool(Some(true))), + (vec![Felt::from(123456789u128)], Primitive::Felt252(Some(Felt::from(123456789)))), + (vec![Felt::from(987654321u128)], Primitive::ClassHash(Some(Felt::from(987654321)))), + ( + vec![Felt::from(123456789u128)], + Primitive::ContractAddress(Some(Felt::from(123456789))), + ), + ]; + + for (serialized, expected) in test_cases { + let mut to_deser = expected; + to_deser.deserialize(&mut serialized.clone()).unwrap(); + assert_eq!(to_deser, expected); + } + } } diff --git a/crates/dojo-types/src/primitive_conversion.rs b/crates/dojo-types/src/primitive_conversion.rs new file mode 100644 index 0000000000..f93be2962e --- /dev/null +++ b/crates/dojo-types/src/primitive_conversion.rs @@ -0,0 +1,105 @@ +// This is a partial implementation of https://github.com/starknet-io/types-rs/pull/74 +// and is required because signed integers are not coverted from Felt correctly with the +// current implementation +// TODO: remove when https://github.com/starknet-io/types-rs/pull/74 is merged. + +use core::convert::TryInto; + +use starknet::core::types::Felt; + +#[derive(Debug, Copy, Clone)] +pub struct PrimitiveFromFeltError; + +impl core::fmt::Display for PrimitiveFromFeltError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Failed to convert `Felt` into primitive type") + } +} + +impl std::error::Error for PrimitiveFromFeltError { + fn description(&self) -> &str { + "Failed to convert `Felt` into primitive type" + } +} + +const MINUS_TWO_BYTES_REPR: [u8; 32] = [ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 8, +]; + +pub trait FromFelt: Sized { + fn try_from_felt(value: Felt) -> Result; +} + +macro_rules! impl_from_felt { + ($into:ty) => { + impl FromFelt for $into { + fn try_from_felt(value: Felt) -> Result { + let size_of_type = core::mem::size_of::<$into>(); + let bytes_le = value.to_bytes_le(); + + if bytes_le[size_of_type..].iter().all(|&v| v == 0) + && bytes_le[size_of_type - 1] <= 0b01111111 + { + Ok(<$into>::from_le_bytes(bytes_le[..size_of_type].try_into().unwrap())) + } else if bytes_le[size_of_type..] == MINUS_TWO_BYTES_REPR[size_of_type..] + && bytes_le[size_of_type - 1] >= 0b10000000 + { + let offsetted_value = + <$into>::from_le_bytes(bytes_le[..size_of_type].try_into().unwrap()); + + offsetted_value.checked_sub(1).ok_or(PrimitiveFromFeltError) + } else if bytes_le[24..] == [17, 0, 0, 0, 0, 0, 0, 8] { + return Ok(-1); + } else { + Err(PrimitiveFromFeltError) + } + } + } + }; +} + +impl_from_felt!(i8); +impl_from_felt!(i16); +impl_from_felt!(i32); +impl_from_felt!(i64); +impl_from_felt!(i128); + +pub fn try_from_felt(value: Felt) -> Result { + T::try_from_felt(value) +} + +#[cfg(test)] +mod tests { + use starknet::core::types::Felt; + + use super::try_from_felt; + + #[test] + fn test_try_from_felt() { + let i_8: i8 = -64; + let felt = Felt::from(i_8); + let signed_integer = try_from_felt::(felt).unwrap(); + assert_eq!(i_8, signed_integer); + + let i_16: i16 = -14293; + let felt = Felt::from(i_16); + let signed_integer = try_from_felt::(felt).unwrap(); + assert_eq!(i_16, signed_integer); + + let i_32: i32 = -194875; + let felt = Felt::from(i_32); + let signed_integer = try_from_felt::(felt).unwrap(); + assert_eq!(i_32, signed_integer); + + let i_64: i64 = -3147483648; + let felt = Felt::from(i_64); + let signed_integer = try_from_felt::(felt).unwrap(); + assert_eq!(i_64, signed_integer); + + let i_128: i128 = -170141183460469231731687303715884105728; + let felt = Felt::from(i_128); + let signed_integer = try_from_felt::(felt).unwrap(); + assert_eq!(i_128, signed_integer); + } +} diff --git a/crates/dojo-types/src/schema.rs b/crates/dojo-types/src/schema.rs index 6556264f15..f5497c06bf 100644 --- a/crates/dojo-types/src/schema.rs +++ b/crates/dojo-types/src/schema.rs @@ -55,8 +55,15 @@ impl Ty { Ty::Primitive(c) => c.to_string(), Ty::Struct(s) => s.name.clone(), Ty::Enum(e) => e.name.replace( - 'T', - &e.options.iter().map(|o| o.ty.name().replace("()", "")).unique().join(""), + "", + &e.options + .iter() + .map(|o| { + let t = o.ty.name().replace("()", ""); + format!("<{}>", t) + }) + .unique() + .join(""), ), Ty::Tuple(tys) => format!("({})", tys.iter().map(|ty| ty.name()).join(", ")), Ty::Array(ty) => format!("Array<{}>", ty[0].name()), @@ -362,6 +369,31 @@ fn format_member(m: &Member) -> String { if let Ty::Primitive(ty) = &m.ty { match ty { + Primitive::I8(value) => { + if let Some(value) = value { + str.push_str(&format!(" = {}", value)); + } + } + Primitive::I16(value) => { + if let Some(value) = value { + str.push_str(&format!(" = {}", value)); + } + } + Primitive::I32(value) => { + if let Some(value) = value { + str.push_str(&format!(" = {}", value)); + } + } + Primitive::I64(value) => { + if let Some(value) = value { + str.push_str(&format!(" = {}", value)); + } + } + Primitive::I128(value) => { + if let Some(value) = value { + str.push_str(&format!(" = {}", value)); + } + } Primitive::U8(value) => { if let Some(value) = value { str.push_str(&format!(" = {}", value)); @@ -427,3 +459,146 @@ fn format_member(m: &Member) -> String { str } + +#[cfg(test)] +mod tests { + use crypto_bigint::U256; + use starknet::core::types::Felt; + + use super::*; + use crate::primitive::Primitive; + + #[test] + fn test_format_member() { + let test_cases = vec![ + ( + Member { + name: "i8_field".to_string(), + ty: Ty::Primitive(Primitive::I8(Some(-42))), + key: false, + }, + " i8_field: i8 = -42", + ), + ( + Member { + name: "i16_field".to_string(), + ty: Ty::Primitive(Primitive::I16(Some(-1000))), + key: false, + }, + " i16_field: i16 = -1000", + ), + ( + Member { + name: "i32_field".to_string(), + ty: Ty::Primitive(Primitive::I32(Some(-100000))), + key: false, + }, + " i32_field: i32 = -100000", + ), + ( + Member { + name: "i64_field".to_string(), + ty: Ty::Primitive(Primitive::I64(Some(-1000000000))), + key: false, + }, + " i64_field: i64 = -1000000000", + ), + ( + Member { + name: "i128_field".to_string(), + ty: Ty::Primitive(Primitive::I128(Some(-1000000000000000000))), + key: false, + }, + " i128_field: i128 = -1000000000000000000", + ), + ( + Member { + name: "u8_field".to_string(), + ty: Ty::Primitive(Primitive::U8(Some(255))), + key: false, + }, + " u8_field: u8 = 255", + ), + ( + Member { + name: "u16_field".to_string(), + ty: Ty::Primitive(Primitive::U16(Some(65535))), + key: false, + }, + " u16_field: u16 = 65535", + ), + ( + Member { + name: "u32_field".to_string(), + ty: Ty::Primitive(Primitive::U32(Some(4294967295))), + key: false, + }, + " u32_field: u32 = 4294967295", + ), + ( + Member { + name: "u64_field".to_string(), + ty: Ty::Primitive(Primitive::U64(Some(18446744073709551615))), + key: false, + }, + " u64_field: u64 = 18446744073709551615", + ), + ( + Member { + name: "u128_field".to_string(), + ty: Ty::Primitive(Primitive::U128(Some( + 340282366920938463463374607431768211455, + ))), + key: false, + }, + " u128_field: u128 = 340282366920938463463374607431768211455", + ), + ( + Member { + name: "u256_field".to_string(), + ty: Ty::Primitive(Primitive::U256(Some(U256::from_u128(123456789_u128)))), + key: false, + }, + " u256_field: u256 = \ + 00000000000000000000000000000000000000000000000000000000075BCD15", + ), + ( + Member { + name: "bool_field".to_string(), + ty: Ty::Primitive(Primitive::Bool(Some(true))), + key: false, + }, + " bool_field: bool = true", + ), + ( + Member { + name: "felt252_field".to_string(), + ty: Ty::Primitive(Primitive::Felt252(Some( + Felt::from_hex("0x123abc").unwrap(), + ))), + key: false, + }, + " felt252_field: felt252 = 0x123abc", + ), + ( + Member { + name: "enum_field".to_string(), + ty: Ty::Enum(Enum { + name: "TestEnum".to_string(), + option: Some(1), + options: vec![ + EnumOption { name: "OptionA".to_string(), ty: Ty::Tuple(vec![]) }, + EnumOption { name: "OptionB".to_string(), ty: Ty::Tuple(vec![]) }, + ], + }), + key: false, + }, + " enum_field: TestEnum = OptionB", + ), + ]; + + for (member, expected) in test_cases { + assert_eq!(format_member(&member), expected); + } + } +} diff --git a/crates/sozo/ops/src/model.rs b/crates/sozo/ops/src/model.rs index ed3a235234..febd87c135 100644 --- a/crates/sozo/ops/src/model.rs +++ b/crates/sozo/ops/src/model.rs @@ -173,6 +173,11 @@ fn get_name_from_schema(schema: &dojo_types::schema::Ty) -> String { dojo_types::schema::Ty::Struct(s) => s.name.clone(), dojo_types::schema::Ty::Enum(e) => e.name.clone(), dojo_types::schema::Ty::Primitive(p) => match p { + dojo_types::primitive::Primitive::I8(_) => "i8".to_string(), + dojo_types::primitive::Primitive::I16(_) => "i16".to_string(), + dojo_types::primitive::Primitive::I32(_) => "i32".to_string(), + dojo_types::primitive::Primitive::I64(_) => "i64".to_string(), + dojo_types::primitive::Primitive::I128(_) => "i128".to_string(), dojo_types::primitive::Primitive::U8(_) => "u8".to_string(), dojo_types::primitive::Primitive::U16(_) => "u16".to_string(), dojo_types::primitive::Primitive::U32(_) => "u32".to_string(), diff --git a/crates/torii/core/src/model.rs b/crates/torii/core/src/model.rs index 28ce30ba2e..c4fea400b3 100644 --- a/crates/torii/core/src/model.rs +++ b/crates/torii/core/src/model.rs @@ -428,13 +428,31 @@ pub fn map_row_to_ty( match ty { Ty::Primitive(primitive) => { match &primitive { - Primitive::Bool(_) => { - let value = row.try_get::(&column_name)?; - primitive.set_bool(Some(value))?; + Primitive::I8(_) => { + let value = row.try_get::(&column_name)?; + primitive.set_i8(Some(value))?; } - Primitive::USize(_) => { - let value = row.try_get::(&column_name)?; - primitive.set_usize(Some(value))?; + Primitive::I16(_) => { + let value = row.try_get::(&column_name)?; + primitive.set_i16(Some(value))?; + } + Primitive::I32(_) => { + let value = row.try_get::(&column_name)?; + primitive.set_i32(Some(value))?; + } + Primitive::I64(_) => { + let value = row.try_get::(&column_name)?; + let hex_str = value.trim_start_matches("0x"); + primitive.set_i64(Some( + i64::from_str_radix(hex_str, 16).map_err(ParseError::ParseIntError)?, + ))?; + } + Primitive::I128(_) => { + let value = row.try_get::(&column_name)?; + let hex_str = value.trim_start_matches("0x"); + primitive.set_i128(Some( + i128::from_str_radix(hex_str, 16).map_err(ParseError::ParseIntError)?, + ))?; } Primitive::U8(_) => { let value = row.try_get::(&column_name)?; @@ -467,6 +485,14 @@ pub fn map_row_to_ty( let hex_str = value.trim_start_matches("0x"); primitive.set_u256(Some(U256::from_be_hex(hex_str)))?; } + Primitive::USize(_) => { + let value = row.try_get::(&column_name)?; + primitive.set_usize(Some(value))?; + } + Primitive::Bool(_) => { + let value = row.try_get::(&column_name)?; + primitive.set_bool(Some(value))?; + } Primitive::Felt252(_) => { let value = row.try_get::(&column_name)?; primitive diff --git a/crates/torii/grpc/src/types/schema.rs b/crates/torii/grpc/src/types/schema.rs index 086fbdf530..8f63d746d0 100644 --- a/crates/torii/grpc/src/types/schema.rs +++ b/crates/torii/grpc/src/types/schema.rs @@ -189,18 +189,25 @@ impl From for proto::types::Primitive { use proto::types::value::ValueType; let value_type = match primitive { - Primitive::Bool(bool) => bool.map(ValueType::BoolValue), + Primitive::I8(i8) => i8.map(|val| ValueType::IntValue(val as i64)), + Primitive::I16(i16) => i16.map(|val| ValueType::IntValue(val as i64)), + Primitive::I32(i32) => i32.map(|val| ValueType::IntValue(val as i64)), + Primitive::I64(i64) => i64.map(ValueType::IntValue), + Primitive::I128(i128) => { + i128.map(|val| ValueType::ByteValue(val.to_be_bytes().to_vec())) + } Primitive::U8(u8) => u8.map(|val| ValueType::UintValue(val as u64)), Primitive::U16(u16) => u16.map(|val| ValueType::UintValue(val as u64)), Primitive::U32(u32) => u32.map(|val| ValueType::UintValue(val as u64)), Primitive::U64(u64) => u64.map(ValueType::UintValue), - Primitive::USize(usize) => usize.map(|val| ValueType::UintValue(val as u64)), Primitive::U128(u128) => { u128.map(|val| ValueType::ByteValue(val.to_be_bytes().to_vec())) } Primitive::U256(u256) => { u256.map(|val| ValueType::ByteValue(val.to_be_bytes().to_vec())) } + Primitive::USize(usize) => usize.map(|val| ValueType::UintValue(val as u64)), + Primitive::Bool(bool) => bool.map(ValueType::BoolValue), Primitive::Felt252(felt) => { felt.map(|val| ValueType::ByteValue(val.to_bytes_be().to_vec())) } diff --git a/crates/torii/types-test/src/contracts.cairo b/crates/torii/types-test/src/contracts.cairo index 88a24731ce..49fbd967ea 100644 --- a/crates/torii/types-test/src/contracts.cairo +++ b/crates/torii/types-test/src/contracts.cairo @@ -61,6 +61,11 @@ mod records { Record { record_id, depth: Depth::Zero, + type_i8: type_felt.try_into().unwrap(), + type_i16: type_felt.try_into().unwrap(), + type_i32: type_felt.try_into().unwrap(), + type_i64: type_felt.try_into().unwrap(), + type_i128: type_felt.try_into().unwrap(), type_u8: record_idx.into(), type_u16: record_idx.into(), type_u32: record_idx.into(), diff --git a/crates/torii/types-test/src/models.cairo b/crates/torii/types-test/src/models.cairo index 17f6fb91a2..489cefd6b1 100644 --- a/crates/torii/types-test/src/models.cairo +++ b/crates/torii/types-test/src/models.cairo @@ -7,6 +7,11 @@ struct Record { #[key] record_id: u32, depth: Depth, + type_i8: i8, + type_i16: i16, + type_i32: i32, + type_i64: i64, + type_i128: i128, type_u8: u8, type_u16: u16, type_u32: u32, @@ -85,7 +90,6 @@ impl DepthIntoFelt252 of Into { } } } - // takes a long time to deploy, uncomment for now // #[derive(Introspect, Copy, Drop, Serde)] // #[dojo::model] @@ -349,3 +353,4 @@ impl DepthIntoFelt252 of Into { // a254: u256, // a255: u256, // } + diff --git a/examples/spawn-and-move/manifests/dev/base/abis/models/dojo_examples-PlayerConfig-3adad785.json b/examples/spawn-and-move/manifests/dev/base/abis/models/dojo_examples-PlayerConfig-3adad785.json index 0185cc11f9..8549c6cad8 100644 --- a/examples/spawn-and-move/manifests/dev/base/abis/models/dojo_examples-PlayerConfig-3adad785.json +++ b/examples/spawn-and-move/manifests/dev/base/abis/models/dojo_examples-PlayerConfig-3adad785.json @@ -377,6 +377,10 @@ { "name": "quantity", "type": "core::integer::u32" + }, + { + "name": "score", + "type": "core::integer::i32" } ] }, diff --git a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml index d570cea3bc..fb28175143 100644 --- a/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml +++ b/examples/spawn-and-move/manifests/dev/base/contracts/dojo_examples-actions-40b6994c.toml @@ -1,6 +1,6 @@ kind = "DojoContract" -class_hash = "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be" -original_class_hash = "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be" +class_hash = "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33" +original_class_hash = "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33" base_class_hash = "0x0" abi = "manifests/dev/base/abis/contracts/dojo_examples-actions-40b6994c.json" reads = [] diff --git a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml index 6bfcf12738..b631214a2e 100644 --- a/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml +++ b/examples/spawn-and-move/manifests/dev/base/models/dojo_examples-PlayerConfig-3adad785.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82" -original_class_hash = "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82" +class_hash = "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7" +original_class_hash = "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7" abi = "manifests/dev/base/abis/models/dojo_examples-PlayerConfig-3adad785.json" tag = "dojo_examples-PlayerConfig" manifest_name = "dojo_examples-PlayerConfig-3adad785" diff --git a/examples/spawn-and-move/manifests/dev/deployment/abis/models/dojo_examples-PlayerConfig-3adad785.json b/examples/spawn-and-move/manifests/dev/deployment/abis/models/dojo_examples-PlayerConfig-3adad785.json index 0185cc11f9..8549c6cad8 100644 --- a/examples/spawn-and-move/manifests/dev/deployment/abis/models/dojo_examples-PlayerConfig-3adad785.json +++ b/examples/spawn-and-move/manifests/dev/deployment/abis/models/dojo_examples-PlayerConfig-3adad785.json @@ -377,6 +377,10 @@ { "name": "quantity", "type": "core::integer::u32" + }, + { + "name": "score", + "type": "core::integer::i32" } ] }, diff --git a/examples/spawn-and-move/manifests/dev/deployment/manifest.json b/examples/spawn-and-move/manifests/dev/deployment/manifest.json index 1fd7c0ef24..ce9072be7e 100644 --- a/examples/spawn-and-move/manifests/dev/deployment/manifest.json +++ b/examples/spawn-and-move/manifests/dev/deployment/manifest.json @@ -1211,8 +1211,8 @@ { "kind": "DojoContract", "address": "0x5c92c8995272a6ae073392c6878fe80cd71ae0be4931bce75f3dbfe24c208a8", - "class_hash": "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be", - "original_class_hash": "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be", + "class_hash": "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33", + "original_class_hash": "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33", "base_class_hash": "0x26a4f5d2d9638877a2648297339275df5eaab0adb3cdf0010887c2dbf2be4", "abi": [ { @@ -4575,8 +4575,8 @@ "key": false } ], - "class_hash": "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82", - "original_class_hash": "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82", + "class_hash": "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7", + "original_class_hash": "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7", "abi": [ { "type": "impl", @@ -4956,6 +4956,10 @@ { "name": "quantity", "type": "core::integer::u32" + }, + { + "name": "score", + "type": "core::integer::i32" } ] }, diff --git a/examples/spawn-and-move/manifests/dev/deployment/manifest.toml b/examples/spawn-and-move/manifests/dev/deployment/manifest.toml index 513b453b78..f6bacb4b73 100644 --- a/examples/spawn-and-move/manifests/dev/deployment/manifest.toml +++ b/examples/spawn-and-move/manifests/dev/deployment/manifest.toml @@ -24,8 +24,8 @@ manifest_name = "dojo-base" [[contracts]] kind = "DojoContract" address = "0x5c92c8995272a6ae073392c6878fe80cd71ae0be4931bce75f3dbfe24c208a8" -class_hash = "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be" -original_class_hash = "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be" +class_hash = "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33" +original_class_hash = "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33" base_class_hash = "0x26a4f5d2d9638877a2648297339275df5eaab0adb3cdf0010887c2dbf2be4" abi = "manifests/dev/deployment/abis/contracts/dojo_examples-actions-40b6994c.json" reads = [] @@ -196,8 +196,8 @@ key = false [[models]] kind = "DojoModel" -class_hash = "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82" -original_class_hash = "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82" +class_hash = "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7" +original_class_hash = "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7" abi = "manifests/dev/deployment/abis/models/dojo_examples-PlayerConfig-3adad785.json" tag = "dojo_examples-PlayerConfig" manifest_name = "dojo_examples-PlayerConfig-3adad785" diff --git a/examples/spawn-and-move/manifests/release/base/abis/models/dojo_examples-PlayerConfig-3adad785.json b/examples/spawn-and-move/manifests/release/base/abis/models/dojo_examples-PlayerConfig-3adad785.json index 0185cc11f9..8549c6cad8 100644 --- a/examples/spawn-and-move/manifests/release/base/abis/models/dojo_examples-PlayerConfig-3adad785.json +++ b/examples/spawn-and-move/manifests/release/base/abis/models/dojo_examples-PlayerConfig-3adad785.json @@ -377,6 +377,10 @@ { "name": "quantity", "type": "core::integer::u32" + }, + { + "name": "score", + "type": "core::integer::i32" } ] }, diff --git a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml index 0a2661f564..445640874c 100644 --- a/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml +++ b/examples/spawn-and-move/manifests/release/base/contracts/dojo_examples-actions-40b6994c.toml @@ -1,6 +1,6 @@ kind = "DojoContract" -class_hash = "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be" -original_class_hash = "0x19d49d8d67dd2e6f73c93eb886105d445ac6983a49950831a325872f8c662be" +class_hash = "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33" +original_class_hash = "0x676eed1c5c6c5f1ddf488914d137e07761d1a320df8a50fbf75ecf209624a33" base_class_hash = "0x0" abi = "manifests/release/base/abis/contracts/dojo_examples-actions-40b6994c.json" reads = [] diff --git a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml index 6fd10ebb99..b19e2f2b73 100644 --- a/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml +++ b/examples/spawn-and-move/manifests/release/base/models/dojo_examples-PlayerConfig-3adad785.toml @@ -1,6 +1,6 @@ kind = "DojoModel" -class_hash = "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82" -original_class_hash = "0x13f8294fde1c2605e9825782ae293b3bae2291e6cbd20d4ef8878ff044d4b82" +class_hash = "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7" +original_class_hash = "0x16141fdae16b43c612d705795e8993ebacd06edb4ec582041b15b6878d65de7" abi = "manifests/release/base/abis/models/dojo_examples-PlayerConfig-3adad785.json" tag = "dojo_examples-PlayerConfig" manifest_name = "dojo_examples-PlayerConfig-3adad785" diff --git a/examples/spawn-and-move/src/actions.cairo b/examples/spawn-and-move/src/actions.cairo index da2b56ce5b..18350b28a0 100644 --- a/examples/spawn-and-move/src/actions.cairo +++ b/examples/spawn-and-move/src/actions.cairo @@ -116,7 +116,8 @@ mod actions { let player = get_caller_address(); let items = array![ - PlayerItem { item_id: 1, quantity: 100 }, PlayerItem { item_id: 2, quantity: 50 } + PlayerItem { item_id: 1, quantity: 100, score: 10 }, + PlayerItem { item_id: 2, quantity: 50, score: -32 } ]; let config = PlayerConfig { player, name, items, favorite_item: Option::Some(1), }; diff --git a/examples/spawn-and-move/src/models.cairo b/examples/spawn-and-move/src/models.cairo index 50da9c93f9..4882eb9ddf 100644 --- a/examples/spawn-and-move/src/models.cairo +++ b/examples/spawn-and-move/src/models.cairo @@ -74,6 +74,7 @@ struct Position { struct PlayerItem { item_id: u32, quantity: u32, + score: i32, } #[derive(Drop, Serde)] diff --git a/scripts/prettier.sh b/scripts/prettier.sh deleted file mode 100644 index adada3aabd..0000000000 --- a/scripts/prettier.sh +++ /dev/null @@ -1,6 +0,0 @@ -#/bin/bash - -# Formats all the markdown and yaml files in the repository. - -prettier --check "**/*.md" -prettier --check "**/*.{yaml,yml}"