Sierra decompiler output can be complex to read and can be enhanced using LLMs. Here is a prompt tested with the Codestral-22B model which improves decompiler output and convert it to Cairo code.
I want to improve the code output by my decompiler, which decompiles an intermediate representation into Cairo code. Cairo is a programming language that uses a syntax similar to Rust.
Cairo’s native data type is a field element (felt). Specifically, the felt252 data type is used to represent a 252-bit integer.
Here is an example of a Cairo Program :
// Calculates fib...
fn fib(a: felt252, b: felt252, n: felt252) -> (felt252, felt252) {
match n {
0 => (a, 0),
_ => {
let (v, count) = fib(b, a + b, n - 1);
(v, count + 1)
},
}
}
And here the corresponding decompiled code :
// Function 1
func examples::fib_counter::fib (v0: felt252, v1: felt252, v2: felt252) -> (Tuple<felt252, felt252>) {
v3 = v2
if (v3 == 0) {
v7 = v1
v8 = v0 + v7
v9 = 1
v10 = v2 - v9
v11 = user@examples::fib_counter::fib(v1, v8, v10)
v12, v13 = struct_deconstruct<Tuple<felt252, felt252>>(v11)
v14 = 1
v15 = v13 + v14
v16 = struct_construct<Tuple<felt252, felt252>>(v12, v15)
return (v16)
} else {
v5 = 0
v6 = struct_construct<Tuple<felt252, felt252>>(v0, v5)
return (v6)
}
}
Here is another example of Cairo code :
#[derive(Drop)]
struct A {
val: u256,
arr: Array<u256>,
}
fn complex_input(
felt_input: felt252,
mut felt_arr_input: Array<felt252>,
mut a_input: A,
mut a_arr_input: Array<A>,
) -> u256 {
let mut r: u256 = felt_input.into();
while let Option::Some(x) = felt_arr_input.pop_front() {
r += x.into();
};
r += a_input.val;
while let Option::Some(x) = a_input.arr.pop_front() {
r += x;
};
while let Option::Some(mut a) = a_arr_input.pop_front() {
r += a.val;
while let Option::Some(x) = a.arr.pop_front() {
r += x;
};
};
r
}
Simplify the following code by removing redundant variables and unnecessary code. No explanation. Help me add short and concise comments for the code snippet in the following code where it is needed. No explanation. Rename the variables in the following code with names corresponding to what they do. No explanation. Rename and simplify the functions in the following code with names corresponding to what they do. No explanation. Optimize the following code to improve readability and simplicity. No explanation.
Convert constants with a corresponding string comments such as : 7891998437966260601762371672023996916393715052535837300 // "Returned data too short"
into their Cairo equivalent : String::from("Returned data too short");.
or :
7733229381460288120802334208475838166080759535023995805565484692595 // "Input too long for arguments" to String::from("Input too long for arguments");
Answer with the improved code. Don't explain anything. Don't create new functions.
Here is the decompiled code to improve :
<Decompiled code to improve>
For example when we decompile minimal_contract__minimal_contract.sierra
using sierra-analyzer
cargo run -- -f examples/sierra/minimal_contract__minimal_contract.sierra
We get this result which can be a little hard to understand and analyze :
// Function 1
func cairo_level_tests::contracts::minimal_contract::minimal_contract::__wrapper__empty (v0: RangeCheck, v1: GasBuiltin, v2: System, v3: core::array::Span::<core::felt252>) -> (RangeCheck, GasBuiltin, System, core::panics::PanicResult::<(core::array::Span::<core::felt252>)>) {
if (withdraw_gas(v0, v1) == 0) {
v35 = Array<felt252>::new()
v36 = 375233589013918064796019 // "Out of gas"
v37 = v35.append(v36)
v38 = struct_construct<core::panics::Panic>()
v39 = struct_construct<Tuple<core::panics::Panic, Array<felt252>>>(v38, v37)
v40 = enum_init<core::panics::PanicResult::<(core::array::Span::<core::felt252>)>, 1>(v39)
return (v6, v7, v2, v40)
} else {
v8 = struct_deconstruct<core::array::Span::<core::felt252>>(v3)
if (array_snapshot_pop_front<felt252>(v8) == 0) {
if (withdraw_gas_all(v4, v5, v18) == 0) {
v29 = Array<felt252>::new()
v30 = 375233589013918064796019 // "Out of gas"
v31 = v29.append(v30)
v32 = struct_construct<core::panics::Panic>()
v33 = struct_construct<Tuple<core::panics::Panic, Array<felt252>>>(v32, v31)
v34 = enum_init<core::panics::PanicResult::<(core::array::Span::<core::felt252>)>, 1>(v33)
return (v21, v22, v2, v34)
} else {
v23 = Array<felt252>::new()
v24, v25 = snapshot_take<Array<felt252>>(v23)
v26 = struct_construct<core::array::Span::<core::felt252>>(v25)
v27 = struct_construct<Tuple<core::array::Span::<core::felt252>>>(v26)
v28 = enum_init<core::panics::PanicResult::<(core::array::Span::<core::felt252>)>, 0>(v27)
return (v19, v20, v2, v28)
}
} else {
v12 = Array<felt252>::new()
v13 = 7733229381460288120802334208475838166080759535023995805565484692595 // "Input too long for arguments"
v14 = v12.append(v13)
v15 = struct_construct<core::panics::Panic>()
v16 = struct_construct<Tuple<core::panics::Panic, Array<felt252>>>(v15, v14)
v17 = enum_init<core::panics::PanicResult::<(core::array::Span::<core::felt252>)>, 1>(v16)
return (v4, v5, v2, v17)
}
}
}
Using the prompt with Codestral-22B we obtain :
// Function: Minimal Contract Wrapper
func cairo_level_tests::contracts::minimal_contract::minimal_contract::__wrapper__empty (gas_check: RangeCheck, gas_builtin: GasBuiltin, sys: System, input_span: core::array::Span::<core::felt252>) -> (RangeCheck, GasBuiltin, System, core::panics::PanicResult::<(core::array::Span::<core::felt252>)>) {
// Check if there is enough gas
if withdraw_gas(gas_check, gas_builtin) == 0 {
// If not, return an out of gas error
return handle_error(gas_check, gas_builtin, sys, String::from("Out of gas"));
} else {
// Deconstruct the input span
let (_, input_array) = struct_deconstruct<core::array::Span::<core::felt252>>(input_span);
// Check if the input is too long
if array_snapshot_pop_front<felt252>(input_array) == 0 {
// If it is, return an input too long error
return handle_error(gas_check, gas_builtin, sys, String::from("Input too long for arguments"));
} else {
// If there is enough gas and the input is not too long, proceed
if withdraw_gas_all(gas_check, gas_builtin, sys) == 0 {
// If there is not enough gas, return an out of gas error
return handle_error(gas_check, gas_builtin, sys, String::from("Out of gas"));
} else {
// If there is enough gas, return the input span
return return_success(gas_check, gas_builtin, sys, input_span);
}
}
}
}
// Function: Handle Error
func handle_error(gas_check: RangeCheck, gas_builtin: GasBuiltin, sys: System, error_message: String) -> (RangeCheck, GasBuiltin, System, core::panics::PanicResult::<(core::array::Span::<core::felt252>)>) {
let error_array = Array<felt252>::new().append(error_message);
let panic_struct = struct_construct<core::panics::Panic>();
let panic_result = struct_construct<Tuple<core::panics::Panic, Array<felt252>>>(panic_struct, error_array);
return (gas_check, gas_builtin, sys, enum_init<core::panics::PanicResult::<(core::array::Span::<core::felt252>)>, 1>(panic_result));
}
// Function: Return Success
func return_success(gas_check: RangeCheck, gas_builtin: GasBuiltin, sys: System, input_span: core::array::Span::<core::felt252>) -> (RangeCheck, GasBuiltin, System, core::panics::PanicResult::<(core::array::Span::<core::felt252>)>) {
let empty_array = Array<felt252>::new();
let (_, snapshot) = snapshot_take<Array<felt252>>(empty_array);
let span_struct = struct_construct<core::array::Span::<core::felt252>>(snapshot);
let success_result = struct_construct<Tuple<core::array::Span::<core::felt252>>>(span_struct);
return (gas_check, gas_builtin, sys, enum_init<core::panics::PanicResult::<(core::array::Span::<core::felt252>)>, 0>(success_result));
}