diff --git a/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs b/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs index 28c9a6988a..1ab8fc9b28 100644 --- a/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs +++ b/crates/dojo/bindgen/src/plugins/typescript/generator/function.rs @@ -35,52 +35,99 @@ impl TsFunctionGenerator { buffer.iter().position(|b| b.contains(fn_wrapper)).unwrap() } + /// Generate string template to build function calldata. + /// * namespace - &str - Token namespace + /// * contract_name - &str - Token contract_name avoid name clashing between different systems + /// * token - &Function - cairo function token + fn build_function_calldata(&self, contract_name: &str, token: &Function) -> String { + format!( + "\tconst build_{contract_name}_{}_calldata = ({}) => {{ +\t\treturn {{ +\t\t\tcontractName: \"{contract_name}\", +\t\t\tentrypoint: \"{}\", +\t\t\tcalldata: [{}], +\t\t}}; +\t}}; +", + token.name.to_case(Case::Camel), + self.get_function_input_args(token).join(", "), + token.name, + self.format_function_calldata(token), + ) + } + + /// Generate string template to build system function + /// We call different methods depending if token is View or External + /// * namespace - &str - Token namespace + /// * contract_name - &str - Token contract_name avoid name clashing between different systems + /// * token - &Function - cairo function token fn generate_system_function( &self, namespace: &str, contract_name: &str, token: &Function, ) -> String { - match token.state_mutability { + let function_calldata = self.build_function_calldata(contract_name, token); + let function_name = format!("{contract_name}_{}", token.name.to_case(Case::Camel)); + let function_call = match token.state_mutability { StateMutability::External => format!( - "\tconst {contract_name}_{} = async ({}) => {{ + "\tconst {function_name} = async ({}) => {{ \t\ttry {{ \t\t\treturn await provider.execute( \t\t\t\tsnAccount, -\t\t\t\t{{ -\t\t\t\t\tcontractName: \"{contract_name}\", -\t\t\t\t\tentrypoint: \"{}\", -\t\t\t\t\tcalldata: [{}], -\t\t\t\t}}, +\t\t\t\tbuild_{function_name}_calldata({}), \t\t\t\t\"{namespace}\", \t\t\t); \t\t}} catch (error) {{ \t\t\tconsole.error(error); +\t\t\tthrow error; \t\t}} \t}};\n", - token.name.to_case(Case::Camel), self.format_function_inputs(token), - token.name, - self.format_function_calldata(token) + self.format_function_calldata(token), ), StateMutability::View => format!( - "\tconst {contract_name}_{} = async ({}) => {{ + "\tconst {function_name} = async ({}) => {{ \t\ttry {{ -\t\t\treturn await provider.call(\"{namespace}\", {{ -\t\t\t\tcontractName: \"{contract_name}\", -\t\t\t\tentrypoint: \"{}\", -\t\t\t\tcalldata: [{}], -\t\t\t}}); +\t\t\treturn await provider.call(\"{namespace}\", build_{function_name}_calldata({}); \t\t}} catch (error) {{ \t\t\tconsole.error(error); +\t\t\tthrow error; \t\t}} \t}};\n", - token.name.to_case(Case::Camel), self.format_function_inputs(token), - token.name, - self.format_function_calldata(token) + self.format_function_calldata(token), ), - } + }; + format!("{}\n{}", function_calldata, function_call) + } + + fn get_function_input_args(&self, token: &Function) -> Vec { + token.inputs.iter().fold(Vec::new(), |mut acc, input| { + let prefix = match &input.1 { + Token::Composite(t) => { + if !token_is_custom_enum(t) + && (t.r#type == CompositeType::Enum + || (t.r#type == CompositeType::Struct + && !t.type_path.starts_with("core")) + || t.r#type == CompositeType::Unknown) + { + "models." + } else { + "" + } + } + _ => "", + }; + let mut type_input = JsPrimitiveInputType::from(&input.1).to_string(); + if type_input.contains("<") { + type_input = type_input.replace("<", format!("<{}", prefix).as_str()); + } else { + type_input = format!("{}{}", prefix, type_input); + } + acc.push(format!("{}: {}", input.0.to_case(Case::Camel), type_input)); + acc + }) } fn format_function_inputs(&self, token: &Function) -> String { @@ -88,35 +135,7 @@ impl TsFunctionGenerator { StateMutability::External => vec!["snAccount: Account | AccountInterface".to_owned()], StateMutability::View => Vec::new(), }; - token - .inputs - .iter() - .fold(inputs, |mut acc, input| { - let prefix = match &input.1 { - Token::Composite(t) => { - if !token_is_custom_enum(t) - && (t.r#type == CompositeType::Enum - || (t.r#type == CompositeType::Struct - && !t.type_path.starts_with("core")) - || t.r#type == CompositeType::Unknown) - { - "models." - } else { - "" - } - } - _ => "", - }; - let mut type_input = JsPrimitiveInputType::from(&input.1).to_string(); - if type_input.contains("<") { - type_input = type_input.replace("<", format!("<{}", prefix).as_str()); - } else { - type_input = format!("{}{}", prefix, type_input); - } - acc.push(format!("{}: {}", input.0.to_case(Case::Camel), type_input)); - acc - }) - .join(", ") + [inputs, self.get_function_input_args(token)].concat().join(", ") } fn format_function_calldata(&self, token: &Function) -> String { @@ -143,12 +162,15 @@ impl TsFunctionGenerator { token: &Function, buffer: &mut Buffer, ) { - let return_token = "\treturn {"; + let function_name = format!("{contract_name}_{}", token.name.to_case(Case::Camel)); + let build_calldata = format!("build{}Calldata", token.name.to_case(Case::Pascal)); + let build_calldata_sc = + format!("build_{contract_name}_{}_calldata", token.name.to_case(Case::Camel)); + let return_token = "\n\n\treturn {"; if !buffer.has(return_token) { buffer.push(format!( - "\treturn {{\n\t\t{}: {{\n\t\t\t{}: {}_{},\n\t\t}},\n\t}};\n}}", - contract_name, - token.name.to_case(Case::Camel), + "\n\n\treturn {{\n\t\t{}: {{\n\t\t\t{}: {function_name},\n\t\t\t{build_calldata}: \ + {build_calldata_sc},\n\t\t}},\n\t}};\n}}", contract_name, token.name.to_case(Case::Camel) )); @@ -171,9 +193,8 @@ impl TsFunctionGenerator { ) { if let Some(insert_pos) = buffer.get_first_before_pos(",", pos, return_idx) { let gen = format!( - "\n\t\t\t{}: {}_{},", - token.name.to_case(Case::Camel), - contract_name, + "\n\t\t\t{}: {function_name},\n\t\t\t{build_calldata}: \ + {build_calldata_sc},", token.name.to_case(Case::Camel) ); // avoid duplicating identifiers. This can occur because some contracts keeps @@ -189,11 +210,10 @@ impl TsFunctionGenerator { // if buffer has return but not contract_name, we append in this object buffer.insert_after( format!( - "\n\t\t{}: {{\n\t\t\t{}: {}_{},\n\t\t}},", - contract_name, - token.name.to_case(Case::Camel), + "\n\t\t{}: {{\n\t\t\t{}: {function_name},\n\t\t\t{build_calldata}: \ + {build_calldata_sc},\n\t\t}},", contract_name, - token.name.to_case(Case::Camel), + token.name.to_case(Case::Camel) ), return_token, ",", @@ -271,20 +291,25 @@ mod tests { fn test_generate_system_function() { let generator = TsFunctionGenerator {}; let function = create_change_theme_function(); - let expected = "\tconst actions_changeTheme = async (snAccount: Account | \ - AccountInterface, value: BigNumberish) => { + let expected = "\tconst build_actions_changeTheme_calldata = (value: BigNumberish) => { +\t\treturn { +\t\t\tcontractName: \"actions\", +\t\t\tentrypoint: \"change_theme\", +\t\t\tcalldata: [value], +\t\t}; +\t}; + +\tconst actions_changeTheme = async (snAccount: Account | AccountInterface, value: BigNumberish) \ + => { \t\ttry { \t\t\treturn await provider.execute( \t\t\t\tsnAccount, -\t\t\t\t{ -\t\t\t\t\tcontractName: \"actions\", -\t\t\t\t\tentrypoint: \"change_theme\", -\t\t\t\t\tcalldata: [value], -\t\t\t\t}, +\t\t\t\tbuild_actions_changeTheme_calldata(value), \t\t\t\t\"onchain_dash\", \t\t\t); \t\t} catch (error) { \t\t\tconsole.error(error); +\t\t\tthrow error; \t\t} \t}; "; @@ -367,9 +392,10 @@ mod tests { generator.setup_function_wrapper_end("actions", &create_change_theme_function(), &mut buff); - let expected = "\treturn { + let expected = "\n\n\treturn { \t\tactions: { \t\t\tchangeTheme: actions_changeTheme, +\t\t\tbuildChangeThemeCalldata: build_actions_changeTheme_calldata, \t\t}, \t}; }"; @@ -382,10 +408,12 @@ mod tests { &create_increate_global_counter_function(), &mut buff, ); - let expected_2 = "\treturn { + let expected_2 = "\n\n\treturn { \t\tactions: { \t\t\tchangeTheme: actions_changeTheme, +\t\t\tbuildChangeThemeCalldata: build_actions_changeTheme_calldata, \t\t\tincreaseGlobalCounter: actions_increaseGlobalCounter, +\t\t\tbuildIncreaseGlobalCounterCalldata: build_actions_increaseGlobalCounter_calldata, \t\t}, \t}; }"; @@ -393,13 +421,16 @@ mod tests { assert_eq!(expected_2, buff[0]); generator.setup_function_wrapper_end("dojo_starter", &create_move_function(), &mut buff); - let expected_3 = "\treturn { + let expected_3 = "\n\n\treturn { \t\tactions: { \t\t\tchangeTheme: actions_changeTheme, +\t\t\tbuildChangeThemeCalldata: build_actions_changeTheme_calldata, \t\t\tincreaseGlobalCounter: actions_increaseGlobalCounter, +\t\t\tbuildIncreaseGlobalCounterCalldata: build_actions_increaseGlobalCounter_calldata, \t\t}, \t\tdojo_starter: { \t\t\tmove: dojo_starter_move, +\t\t\tbuildMoveCalldata: build_dojo_starter_move_calldata, \t\t}, \t}; }"; @@ -414,9 +445,10 @@ mod tests { generator.setup_function_wrapper_end("actions", &create_change_theme_function(), &mut buff); - let expected = "\treturn { + let expected = "\n\n\treturn { \t\tactions: { \t\t\tchangeTheme: actions_changeTheme, +\t\t\tbuildChangeThemeCalldata: build_actions_changeTheme_calldata, \t\t}, \t}; }";