diff --git a/contracts/src/gen.cairo b/contracts/src/gen.cairo index fe47df7..8857a33 100644 --- a/contracts/src/gen.cairo +++ b/contracts/src/gen.cairo @@ -8,6 +8,16 @@ mod gen { v2: felt252, } + #[derive(Serde, Drop)] + struct PlainStruct { + f1: u8, + f2: u16, + f3: u32, + f4: u64, + f5: u128, + f6: felt252, + } + #[derive(Serde, Drop)] struct MyStruct { f1: felt252, @@ -15,6 +25,19 @@ mod gen { f3: felt252, } + #[derive(Serde, Drop)] + enum MyEnum { + One: u8, + Two: u16, + Three: u32, + Four: u64, + Five: u128, + Six: felt252, + Seven: i32, + Eight: i64, + Nine: i128, + } + #[external(v0)] fn func1(ref self: ContractState, a: MyStruct) { self.v1.write(a.f1); @@ -31,4 +54,12 @@ mod gen { fn read(self: @ContractState) -> (felt252, felt252) { (self.v1.read(), self.v2.read()) } + + #[external(v0)] + fn func3(self: @ContractState, _a: PlainStruct) { + } + + #[external(v0)] + fn func4(self: @ContractState, _a: MyEnum) { + } } diff --git a/crates/cairo-serde/src/lib.rs b/crates/cairo-serde/src/lib.rs index 034d753..0022cd8 100644 --- a/crates/cairo-serde/src/lib.rs +++ b/crates/cairo-serde/src/lib.rs @@ -51,3 +51,12 @@ pub trait CairoSerde { /// Deserializes an array of felts into the given type. fn cairo_deserialize(felts: &[Felt], offset: usize) -> Result; } + +/// Serialize a value as a hex string. +pub fn serialize_as_hex(value: &T, serializer: S) -> std::result::Result +where + S: serde::Serializer, + T: serde::Serialize + std::fmt::LowerHex, +{ + serializer.serialize_str(&format!("{:#x}", value)) +} diff --git a/crates/rs/src/expand/enum.rs b/crates/rs/src/expand/enum.rs index a61542a..405b169 100644 --- a/crates/rs/src/expand/enum.rs +++ b/crates/rs/src/expand/enum.rs @@ -22,10 +22,12 @@ impl CairoEnum { let name = utils::str_to_ident(&inner.name); let ty = utils::str_to_type(&inner.token.to_rust_type()); + let serde = utils::serde_hex_derive(&inner.token.to_rust_type()); + if inner.token.type_name() == "()" { - variants.push(quote!(#name)); + variants.push(quote!(#serde #name)); } else { - variants.push(quote!(#name(#ty))); + variants.push(quote!(#serde #name(#ty))); } } diff --git a/crates/rs/src/expand/struct.rs b/crates/rs/src/expand/struct.rs index 606d410..7b952ba 100644 --- a/crates/rs/src/expand/struct.rs +++ b/crates/rs/src/expand/struct.rs @@ -21,17 +21,19 @@ impl CairoStruct { let name = utils::str_to_ident(&inner.name); let ty = utils::str_to_type(&inner.token.to_rust_type()); + let serde = utils::serde_hex_derive(&inner.token.to_rust_type()); + // r#{name} is not a valid identifier, thus we can't create an ident. // And with proc macro 2, we cannot do `quote!(r##name)`. // TODO: this needs to be done more elegantly... if &inner.name == "type" { - members.push(quote!(r#type: #ty)); + members.push(quote!(#serde pub r#type: #ty)); } else if &inner.name == "move" { - members.push(quote!(r#move: #ty)); + members.push(quote!(#serde pub r#move: #ty)); } else if &inner.name == "final" { - members.push(quote!(r#final: #ty)); + members.push(quote!(#serde pub r#final: #ty)); } else { - members.push(quote!(#name: #ty)); + members.push(quote!(#serde pub #name: #ty)); } } @@ -57,14 +59,14 @@ impl CairoStruct { quote! { #[derive(#(#internal_derives,)*)] pub struct #struct_name<#(#gen_args),*> { - #(pub #members),* + #(#members),* } } } else { quote! { #[derive(#(#internal_derives,)*)] pub struct #struct_name { - #(pub #members),* + #(#members),* } } } diff --git a/crates/rs/src/expand/utils.rs b/crates/rs/src/expand/utils.rs index a38951a..aa2abc5 100644 --- a/crates/rs/src/expand/utils.rs +++ b/crates/rs/src/expand/utils.rs @@ -74,3 +74,25 @@ pub fn rust_associated_type_gen_args(entity_name: &Ident, gen_args: &[Ident]) -> quote!(type RustType = #entity_name<#(#gen_args_rust),*>;) } + +/// To simplify the serde interop with client in javascript, +/// we use the hex format for all the types greater than u32. +/// IEEE 754 standard for floating-point arithmetic, +/// which can safely represent integers up to 2^53 - 1, which is what Javascript uses. +#[inline] +fn is_serde_hex_int(ty: &str) -> bool { + ty == "u128" || ty == "u64" || ty == "i128" || ty == "i64" +} + +/// Serde derive for hex serialization of struct member or enum variant. +pub fn serde_hex_derive(ty: &str) -> TokenStream2 { + let serde_path = format!("{}::serialize_as_hex", cainome_cairo_serde_path()); + + if is_serde_hex_int(ty) { + quote! { + #[serde(serialize_with = #serde_path)] + } + } else { + quote!() + } +} diff --git a/examples/cairo_serde_derive.rs b/examples/cairo_serde_derive.rs index 9fd4cb2..7c0b094 100644 --- a/examples/cairo_serde_derive.rs +++ b/examples/cairo_serde_derive.rs @@ -1,20 +1,23 @@ use cainome_cairo_serde::CairoSerde; use cainome_cairo_serde_derive::CairoSerde; +use serde::Serialize; use starknet_types_core::felt::Felt; -#[derive(Debug, CairoSerde, PartialEq)] +#[derive(Debug, CairoSerde, PartialEq, Serialize)] struct ExampleSimple { x: Vec, y: Felt, + #[serde(serialize_with = "cainome_cairo_serde::serialize_as_hex")] + z: u128, } -#[derive(Debug, CairoSerde, PartialEq)] +#[derive(Debug, CairoSerde, PartialEq, Serialize)] struct ExampleNested { x: Felt, y: ExampleSimple, } -#[derive(Debug, CairoSerde, PartialEq)] +#[derive(Debug, CairoSerde, PartialEq, Serialize)] struct ExampleTuple(ExampleNested, Vec); #[derive(Debug, CairoSerde, PartialEq)] @@ -32,16 +35,21 @@ fn main() { y: ExampleSimple { x: vec![Felt::from(2), Felt::from(3)], y: Felt::from(4), + z: 1729281360, }, }, vec![Felt::from(1)], ); + let s = serde_json::to_string(&tuple).unwrap(); + println!("s = {}", s); + let example = ExampleEnum::Struct { x: tuple, y: ExampleSimple { x: vec![Felt::from(5), Felt::from(6)], y: Felt::from(7), + z: 1729281360, }, }; diff --git a/examples/structs.rs b/examples/structs.rs index deeac0b..8b97f74 100644 --- a/examples/structs.rs +++ b/examples/structs.rs @@ -1,22 +1,51 @@ use cainome::rs::abigen; - use starknet::core::types::Felt; abigen!( MyContract, "./contracts/abi/gen.abi.json", - derives(Debug, Clone) + derives(Debug, Clone, serde::Serialize) ); #[tokio::main] async fn main() { - let s = MyStruct:: { - f1: Felt::ONE, - f2: Felt::TWO, - f3: Felt::THREE, + let s = PlainStruct { + f1: 1, + f2: 2, + f3: 3, + f4: 4, + f5: 5, + f6: Felt::from(6), }; - println!("{:?}", s); + println!("{}", serde_json::to_string(&s).unwrap()); let _s2 = s.clone(); + + let e = MyEnum::One(1_u8); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Two(1_u16); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Three(1_u32); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Four(1_u64); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Five(1_u128); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Six(Felt::from(6)); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Seven(-1_i32); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Eight(-1_i64); + println!("{}", serde_json::to_string(&e).unwrap()); + + let e = MyEnum::Nine(-1_i128); + println!("{}", serde_json::to_string(&e).unwrap()); }