Skip to content

Commit 1ebcb04

Browse files
blockifier_test_utils: add create_multicall_calldata helper (#14108)
Serializes a sequence of calls as `Array<Call>` for use as Cairo 1 account multicall calldata. Matches `starknet_rust::accounts::ExecutionEncoding::New`. Replaces ad-hoc serialization in `prove_and_verify_multicall_tx` and the private `single_multicall_data` helper in `cende_blob_regression_test`. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1b941fe commit 1ebcb04

3 files changed

Lines changed: 23 additions & 26 deletions

File tree

crates/blockifier_test_utils/src/calldata.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ pub fn create_calldata(
3737
)
3838
}
3939

40+
/// Serializes calls as the canonical Cairo 1 `Array<Call>` payload:
41+
/// `[num_calls, (to, selector, calldata_len, *calldata)*]`.
42+
pub fn create_multicall_calldata(calls: &[(ContractAddress, &str, &[Felt])]) -> Vec<Felt> {
43+
let mut buf = vec![felt!(u64_from_usize(calls.len()))];
44+
for (to, entry_point_name, args) in calls {
45+
buf.push(*to.0.key());
46+
buf.push(selector_from_name(entry_point_name).0);
47+
buf.push(felt!(u64_from_usize(args.len())));
48+
buf.extend_from_slice(args);
49+
}
50+
buf
51+
}
52+
4053
/// Calldata for a trivial entry point in the [`crate::contracts::FeatureContract`] TestContract.
4154
/// The calldata is formatted for using the featured contracts AccountWithLongValidate or
4255
/// AccountWithoutValidations as account contract.

crates/central_systest_blobs/src/cende_blob_regression_test.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use blockifier::transaction::account_transaction::AccountTransaction as Blockifi
3535
use blockifier::transaction::transaction_execution::Transaction as BlockifierTx;
3636
use blockifier::transaction::transactions::ExecutableTransaction;
3737
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
38-
use blockifier_test_utils::calldata::create_calldata;
38+
use blockifier_test_utils::calldata::create_multicall_calldata;
3939
use blockifier_test_utils::contracts::FeatureContract;
4040
use expect_test::{expect, expect_file, Expect};
4141
use google_cloud_storage::client::{Client, ClientConfig};
@@ -447,15 +447,6 @@ impl BlobFactory {
447447
TransactionSignature(Arc::new(vec![sig.r, sig.s]))
448448
}
449449

450-
fn single_multicall_data(
451-
address: ContractAddress,
452-
function_name: &str,
453-
calldata: &[Felt],
454-
) -> Calldata {
455-
let single_calldata = create_calldata(address, function_name, calldata);
456-
Calldata(Arc::new([vec![Felt::ONE], single_calldata.0.as_slice().to_vec()].concat()))
457-
}
458-
459450
/// If the sender address is None, create a bootstrap declare tx.
460451
/// Otherwise, create a regular declare tx (with fees).
461452
fn make_declare_tx(&mut self, contract: FeatureContract, sender: Option<ContractAddress>) {
@@ -603,7 +594,8 @@ impl BlobFactory {
603594
} else {
604595
AllResourceBounds::new_unlimited_gas_no_fee_enforcement()
605596
};
606-
let calldata = Self::single_multicall_data(address, function_name, calldata);
597+
let calldata =
598+
Calldata(create_multicall_calldata(&[(address, function_name, calldata)]).into());
607599
let rpc_tx_unsigned = InternalRpcInvokeTransactionV3 {
608600
sender_address: *OPERATOR_ADDRESS,
609601
calldata,

crates/starknet_os_flow_tests/src/virtual_os_test.rs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use blockifier::test_utils::dict_state_reader::DictStateReader;
55
use blockifier::test_utils::get_valid_virtual_os_program_hash;
66
use blockifier::transaction::test_utils::ExpectedExecutionInfo;
77
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
8-
use blockifier_test_utils::calldata::create_calldata;
8+
use blockifier_test_utils::calldata::{create_calldata, create_multicall_calldata};
99
use blockifier_test_utils::contracts::FeatureContract;
1010
use rstest::rstest;
1111
use starknet_api::abi::abi_utils::selector_from_name;
@@ -285,21 +285,13 @@ async fn prove_and_verify_multicall_tx() {
285285
TestBuilder::create_standard_virtual([(test_contract, calldata![Felt::ONE, Felt::TWO])])
286286
.await;
287287

288-
// Appends a single `Call` entry to `multi_call_args` as
289-
// `[to, selector, calldata_len, *calldata]` and bumps the leading `num_calls`
290-
// counter. All inner calls in this test target the same test contract.
291-
let mut multi_call_args: Vec<Felt> = vec![Felt::ZERO];
292-
let mut serialize_call = |func_name: &str, args: &[Felt]| {
293-
multi_call_args[0] += Felt::ONE;
294-
multi_call_args.push(*contract_address.0.key());
295-
multi_call_args.push(selector_from_name(func_name).0);
296-
multi_call_args.push(Felt::from(args.len()));
297-
multi_call_args.extend_from_slice(args);
298-
};
299-
300288
// TODO(Yoni): add more inner calls (e.g. sha256, secp256k1, send_message_to_l1).
301-
serialize_call("test_keccak", &[]);
302-
serialize_call("test_ec_op", &[]);
289+
// TODO(Yoni): restore the keccak inner call once the keccak syscall is allowed in
290+
// virtual OS mode (added in a follow-up PR stacked on top of this one).
291+
let multi_call_args = create_multicall_calldata(&[
292+
// (contract_address, "test_keccak", &[]),
293+
(contract_address, "test_ec_op", &[]),
294+
]);
303295

304296
// The dummy account's `__execute__(contract_address, selector, calldata)` forwards
305297
// to its own `multi_call` entry point.

0 commit comments

Comments
 (0)