Skip to content

Commit

Permalink
feat: split calldata and provider call (#2852)
Browse files Browse the repository at this point in the history
  • Loading branch information
MartianGreed authored Dec 31, 2024
1 parent 1c3ff08 commit d21b1a1
Showing 1 changed file with 103 additions and 71 deletions.
174 changes: 103 additions & 71 deletions crates/dojo/bindgen/src/plugins/typescript/generator/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,88 +35,107 @@ 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<String> {
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 {
let inputs = match token.state_mutability {
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 {
Expand All @@ -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)
));
Expand All @@ -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
Expand All @@ -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,
",",
Expand Down Expand Up @@ -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};
";
Expand Down Expand Up @@ -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};
}";
Expand All @@ -382,24 +408,29 @@ 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};
}";
assert_eq!(1, buff.len());
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};
}";
Expand All @@ -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};
}";
Expand Down

0 comments on commit d21b1a1

Please sign in to comment.