Skip to content

Commit 9e0b71c

Browse files
authored
Always use data transformer unless --calldata is passed (#3314)
<!-- Reference any GitHub issues resolved by this PR --> Closes #2760 ## Introduced changes <!-- A brief description of the changes --> - Always use data transformer unless --calldata is passed ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md`
1 parent 461314d commit 9e0b71c

File tree

11 files changed

+113
-35
lines changed

11 files changed

+113
-35
lines changed

crates/data-transformer/src/reverse_transformer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use starknet_types_core::felt::Felt;
88

99
#[derive(Debug, thiserror::Error)]
1010
pub enum ReverseTransformError {
11-
#[error(r#"Function with selector "{0}" not found in ABI of the contract"#)]
11+
#[error(r#"Function with selector "{0:#x}" not found in ABI of the contract"#)]
1212
FunctionNotFound(Felt),
1313
#[error(transparent)]
1414
TransformationError(#[from] TransformationError),

crates/data-transformer/src/shared/extraction.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ pub fn extract_function_from_selector(
66
abi: &[AbiEntry],
77
searched_selector: Felt,
88
) -> Option<AbiFunction> {
9+
const CONSTRUCTOR_AS_SELECTOR: Felt = Felt::from_hex_unchecked(
10+
"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194",
11+
);
12+
13+
search_for_function(abi, searched_selector)
14+
// If the user doesn't explicitly define a constructor in the contract,
15+
// it won't be present in the ABI. In such cases, an implicit constructor
16+
// with no arguments is assumed.
17+
.or_else(|| (searched_selector == CONSTRUCTOR_AS_SELECTOR).then(default_constructor))
18+
}
19+
20+
fn default_constructor() -> AbiFunction {
21+
AbiFunction {
22+
name: "constructor".to_string(),
23+
inputs: vec![],
24+
outputs: vec![],
25+
state_mutability: StateMutability::View,
26+
}
27+
}
28+
29+
fn search_for_function(abi: &[AbiEntry], searched_selector: Felt) -> Option<AbiFunction> {
930
abi.iter().find_map(|entry| match entry {
1031
AbiEntry::Function(func) => {
1132
let selector = get_selector_from_name(&func.name).ok()?;
@@ -23,9 +44,7 @@ pub fn extract_function_from_selector(
2344
state_mutability: StateMutability::View,
2445
})
2546
}
26-
AbiEntry::Interface(interface) => {
27-
extract_function_from_selector(&interface.items, searched_selector)
28-
}
47+
AbiEntry::Interface(interface) => search_for_function(&interface.items, searched_selector),
2948
_ => None,
3049
})
3150
}

crates/data-transformer/src/transformer/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use starknet::core::types::contract::{AbiEntry, AbiFunction};
1414
/// Interpret `calldata` as a comma-separated series of expressions in Cairo syntax and serialize it
1515
pub fn transform(calldata: &str, abi: &[AbiEntry], function_selector: &Felt) -> Result<Vec<Felt>> {
1616
let function = extract_function_from_selector(abi, *function_selector).with_context(|| {
17-
format!(r#"Function with selector "{function_selector}" not found in ABI of the contract"#)
17+
format!(
18+
r#"Function with selector "{function_selector:#x}" not found in ABI of the contract"#
19+
)
1820
})?;
1921

2022
let db = SimpleParserDatabase::default();
@@ -25,6 +27,9 @@ pub fn transform(calldata: &str, abi: &[AbiEntry], function_selector: &Felt) ->
2527
}
2628

2729
fn split_expressions(input: &str, db: &SimpleParserDatabase) -> Result<Vec<Expr>> {
30+
if input.is_empty() {
31+
return Ok(Vec::new());
32+
}
2833
// We need to convert our comma-separated string of expressions into something that is a valid
2934
// Cairo expression, so we can parse it.
3035
//

crates/data-transformer/tests/data/data_transformer/src/lib.cairo

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub trait IDataTransformer<TContractState> {
6262
) -> (BitArray, alexandria_data_structures::bit_array::BitArray);
6363
fn span_fn(ref self: TContractState, a: Span<felt252>) -> Span<felt252>;
6464
fn multiple_signed_fn(ref self: TContractState, a: i32, b: i8);
65+
fn no_args_fn(ref self: TContractState);
6566
}
6667

6768
#[starknet::contract]
@@ -129,5 +130,12 @@ mod DataTransformer {
129130
a
130131
}
131132
fn multiple_signed_fn(ref self: ContractState, a: i32, b: i8) {}
133+
fn no_args_fn(ref self: ContractState) {}
132134
}
133135
}
136+
137+
#[starknet::contract]
138+
mod DataTransformerNoConstructor {
139+
#[storage]
140+
struct Storage {}
141+
}

crates/data-transformer/tests/integration/identity.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use test_case::test_case;
2222
#[test_case("Enum::Two(128_u128)", "enum_fn"; "enum_tuple")]
2323
#[test_case("Enum::Three(NestedStructWithField { a: SimpleStruct { a: 0x7b }, b: 0xea })", "enum_fn"; "enum_nested")]
2424
#[test_case(r#"ComplexStruct { a: NestedStructWithField { a: SimpleStruct { a: 0x1 }, b: 0x2 }, b: 0x3, c: 4_u8, d: 5_i32, e: Enum::Two(6_u128), f: "seven", g: array![0x8, 0x9], h: 10_u256, i: (11_i128, 12_u128) }"#, "complex_struct_fn"; "complex_struct")]
25+
#[test_case("", "no_args_fn"; "no_arguments_function")]
2526
#[tokio::test]
2627
async fn test_check_for_identity(calldata: &str, selector: &str) {
2728
let abi = get_abi().await;

crates/data-transformer/tests/integration/mod.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,29 @@ mod identity;
1010
mod reverse_transformer;
1111
mod transformer;
1212

13-
// Class hash of the declared contract from /tests/data/data_transformer
13+
/// Class hash of the declared `DataTransformer` contract from `/tests/data/data_transformer`
1414
const TEST_CLASS_HASH: Felt =
15-
Felt::from_hex_unchecked("0x02978c91b2c3d47cba2103d40280a3601b90ed93a59cebc2ad61c6d1dab5e10a");
15+
Felt::from_hex_unchecked("0x071b56ab58087fd00a0b4ddcdfecb727ae11d1674a4a0f5af7c30f9bb2f7150e");
16+
17+
/// Class hash of the declared `DataTransformerNoConstructor` contract from `/tests/data/data_transformer`
18+
const NO_CONSTRUCTOR_CLASS_HASH: Felt =
19+
Felt::from_hex_unchecked("0x051d0347d3bfcd87eea5175994f55158a24b003370d8c83d2c430f663eceb08d");
1620

1721
static CLASS: OnceCell<ContractClass> = OnceCell::const_new();
1822

19-
async fn init_class() -> ContractClass {
23+
async fn init_class(class_hash: Felt) -> ContractClass {
2024
let client = JsonRpcClient::new(HttpTransport::new(
2125
Url::parse("http://188.34.188.184:7070/rpc/v0_8").unwrap(),
2226
));
2327

2428
client
25-
.get_class(BlockId::Tag(BlockTag::Latest), TEST_CLASS_HASH)
29+
.get_class(BlockId::Tag(BlockTag::Latest), class_hash)
2630
.await
2731
.unwrap()
2832
}
2933

3034
async fn get_abi() -> Vec<AbiEntry> {
31-
let class = CLASS.get_or_init(init_class).await;
35+
let class = CLASS.get_or_init(|| init_class(TEST_CLASS_HASH)).await;
3236
let ContractClass::Sierra(sierra_class) = class else {
3337
panic!("Expected Sierra class, but got legacy Sierra class")
3438
};

crates/data-transformer/tests/integration/reverse_transformer.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use crate::integration::get_abi;
1+
use crate::integration::{NO_CONSTRUCTOR_CLASS_HASH, get_abi, init_class};
22
use data_transformer::{reverse_transform_input, reverse_transform_output};
33
use itertools::Itertools;
44
use primitive_types::U256;
5+
use starknet::core::types::ContractClass;
6+
use starknet::core::types::contract::AbiEntry;
57
use starknet::core::utils::get_selector_from_name;
68
use starknet_types_core::felt::Felt;
79

@@ -339,3 +341,26 @@ async fn test_multiple_signed_max() {
339341
)
340342
.await;
341343
}
344+
345+
#[tokio::test]
346+
async fn test_no_argument_function() {
347+
assert_reverse_transformation(&[], "no_args_fn", "", None).await;
348+
}
349+
350+
#[tokio::test]
351+
async fn test_implicit_contract_constructor() {
352+
let class = init_class(NO_CONSTRUCTOR_CLASS_HASH).await;
353+
let ContractClass::Sierra(sierra_class) = class else {
354+
panic!("Expected Sierra class, but got legacy Sierra class")
355+
};
356+
357+
let abi: Vec<AbiEntry> = serde_json::from_str(sierra_class.abi.as_str()).unwrap();
358+
359+
let result =
360+
reverse_transform_input(&[], &abi, &get_selector_from_name("constructor").unwrap())
361+
.unwrap();
362+
363+
let expected_output = "";
364+
365+
assert_eq!(result, expected_output);
366+
}

crates/data-transformer/tests/integration/transformer.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::integration::get_abi;
1+
use crate::integration::{NO_CONSTRUCTOR_CLASS_HASH, get_abi, init_class};
22
use core::fmt;
33
use data_transformer::transform;
44
use indoc::indoc;
@@ -43,7 +43,7 @@ async fn test_function_not_found() {
4343

4444
result.unwrap_err().assert_contains(
4545
format!(
46-
r#"Function with selector "{}" not found in ABI of the contract"#,
46+
r#"Function with selector "{:#x}" not found in ABI of the contract"#,
4747
get_selector_from_name(selector).unwrap()
4848
)
4949
.as_str(),
@@ -540,6 +540,31 @@ async fn test_happy_case_contract_constructor() {
540540
assert_eq!(result, expected_output);
541541
}
542542

543+
#[tokio::test]
544+
async fn test_happy_case_no_argument_function() {
545+
let result = run_transformer("", "no_args_fn").await.unwrap();
546+
547+
let expected_output = [];
548+
549+
assert_eq!(result, expected_output);
550+
}
551+
552+
#[tokio::test]
553+
async fn test_happy_case_implicit_contract_constructor() {
554+
let class = init_class(NO_CONSTRUCTOR_CLASS_HASH).await;
555+
let ContractClass::Sierra(sierra_class) = class else {
556+
panic!("Expected Sierra class, but got legacy Sierra class")
557+
};
558+
559+
let abi: Vec<AbiEntry> = serde_json::from_str(sierra_class.abi.as_str()).unwrap();
560+
561+
let result = transform("", &abi, &get_selector_from_name("constructor").unwrap()).unwrap();
562+
563+
let expected_output = [];
564+
565+
assert_eq!(result, expected_output);
566+
}
567+
543568
#[tokio::test]
544569
async fn test_external_enum_function_ambiguous_enum_name_cairo_expression_input() {
545570
// https://sepolia.starkscan.co/class/0x019ea00ebe2d83fb210fbd6f52c302b83c69e3c8c934f9404c87861e9d3aebbc#code

crates/sncast/src/main.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,7 @@ impl Arguments {
175175
contract_class: ContractClass,
176176
selector: &Felt,
177177
) -> Result<Vec<Felt>> {
178-
if let Some(arguments) = self.arguments {
179-
let ContractClass::Sierra(sierra_class) = contract_class else {
180-
bail!("Transformation of arguments is not available for Cairo Zero contracts")
181-
};
182-
183-
let abi: Vec<AbiEntry> = serde_json::from_str(sierra_class.abi.as_str())
184-
.context("Couldn't deserialize ABI received from network")?;
185-
186-
transform(&arguments, &abi, selector)
187-
} else if let Some(calldata) = self.calldata {
178+
if let Some(calldata) = self.calldata {
188179
calldata
189180
.iter()
190181
.map(|data| {
@@ -194,7 +185,14 @@ impl Arguments {
194185
})
195186
.collect()
196187
} else {
197-
Ok(vec![])
188+
let ContractClass::Sierra(sierra_class) = contract_class else {
189+
bail!("Transformation of arguments is not available for Cairo Zero contracts")
190+
};
191+
192+
let abi: Vec<AbiEntry> = serde_json::from_str(sierra_class.abi.as_str())
193+
.context("Couldn't deserialize ABI received from network")?;
194+
195+
transform(&self.arguments.unwrap_or_default(), &abi, selector)
198196
}
199197
}
200198
}

crates/sncast/tests/e2e/call.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,11 @@ fn test_wrong_function_name() {
141141
];
142142

143143
let snapbox = runner(&args);
144-
let output = snapbox.assert().success();
144+
let output = snapbox.assert().failure();
145145

146146
assert_stderr_contains(
147147
output,
148-
indoc! {r"
149-
command: call
150-
error: Requested entrypoint does not exist in the contract
151-
"},
148+
r#"Error: Function with selector "0x2924aec1f107eca35a5dc447cee68cc6985fe404841c9aad477adfcbe596d0a" not found in ABI of the contract"#,
152149
);
153150
}
154151

crates/sncast/tests/e2e/invoke.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ async fn test_contract_does_not_exist() {
198198
);
199199
}
200200

201-
// TODO(#3116): Before, the error message included 'ENTRYPOINT_NOT_FOUND', but now it's an undecoded felt.
202201
#[test]
203202
fn test_wrong_function_name() {
204203
let args = vec![
@@ -216,14 +215,11 @@ fn test_wrong_function_name() {
216215
];
217216

218217
let snapbox = runner(&args);
219-
let output = snapbox.assert().success();
218+
let output = snapbox.assert().failure();
220219

221220
assert_stderr_contains(
222221
output,
223-
indoc! {"
224-
command: invoke
225-
error: Transaction execution error [..]0x454e545259504f494e545f4e4f545f464f554e44[..]
226-
"},
222+
r#"Error: Function with selector "0x2e0f845a8d0319c5c37d558023299beec2a0155d415f41cca140a09e6877c67" not found in ABI of the contract"#,
227223
);
228224
}
229225

0 commit comments

Comments
 (0)