Calling Contracts
Build, simulate, and send contract function calls via aztec-contract.
Runnable Examples
examples/simulate_profile_send.rs— simulate → profile → send for a single call.examples/private_token_transfer.rs— private function call end to end.examples/public_storage.rs— public function + public storage read.examples/note_getter.rs— reading owned notes from the PXE.
Typical Flow
use aztec_rs::abi::{AbiValue, ContractArtifact};
use aztec_rs::contract::Contract;
let artifact: ContractArtifact = /* load_artifact(...) */;
let handle = Contract::at(token_address, artifact, wallet.clone());
// Build a call — arity and types are validated against the artifact.
let call = handle.method("transfer", vec![
AbiValue::Field(sender.into()),
AbiValue::Field(recipient.into()),
AbiValue::Integer(amount),
])?;
// Optional: simulate + profile
let sim = call.simulate(Default::default()).await?;
let prof = call.profile(Default::default()).await?;
// Submit; SendOptions picks the fee payment method.
let sent = call.send(SendOptions { from: sender, ..Default::default() }).await?;
println!("tx hash: {}", sent.tx_hash);
Choosing the Right Path
| Function kind | Method |
|---|---|
| Private | handle.method(...).send(...) |
| Public | handle.method(...).send(...) (same) |
| Utility (off-chain) | Wallet::execute_utility(call, opts) or handle.method(...).simulate(...) |
Wallet::execute_utility does not produce a transaction; it runs the utility inside the PXE and returns the decoded values + logs.
Batch Calls
BatchCall bundles several calls into a single transaction.
Use it when multiple calls share authorization or must be atomic.
Attaching Authwits / Capsules
let call = handle.method("spend_on_behalf", args)?
.with(vec![authwit], vec![capsule]);
ContractFunctionInteraction::with(auth_witnesses, capsules) attaches additional authorization or capsule data before simulation / send.
Full Runnable Example
Source: examples/simulate_profile_send.rs.
//! Compare simulation, profiling, and sending for the same call.
#![allow(clippy::print_stdout, clippy::wildcard_imports)]
mod common;
use common::*;
#[tokio::main]
async fn main() -> Result<(), aztec_rs::Error> {
let Some((wallet, owner)) = setup_wallet(TEST_ACCOUNT_0).await else {
return Err(aztec_rs::Error::InvalidData(format!(
"node not reachable at {}",
node_url()
)));
};
let (address, artifact, _) = deploy_contract(
&wallet,
load_stateful_test_artifact(),
vec![abi_address(owner), AbiValue::Field(Fr::from(1u64))],
owner,
)
.await?;
let call = build_call(
&artifact,
address,
"increment_public_value_no_init_check",
vec![abi_address(owner), AbiValue::Field(Fr::from(5u64))],
);
let payload = ExecutionPayload {
calls: vec![call],
..Default::default()
};
let sim = wallet
.simulate_tx(
payload.clone(),
SimulateOptions {
from: owner,
estimate_gas: true,
..Default::default()
},
)
.await?;
let gas_limits = get_gas_limits(&sim, None);
let profile = wallet
.profile_tx(
payload.clone(),
ProfileOptions {
from: owner,
profile_mode: Some(ProfileMode::Full),
..Default::default()
},
)
.await?;
let tx_hash = wallet
.send_tx(
payload,
SendOptions {
from: owner,
gas_settings: Some(GasSettings {
gas_limits: Some(gas_limits.gas_limits.clone()),
teardown_gas_limits: Some(gas_limits.teardown_gas_limits.clone()),
..Default::default()
}),
..Default::default()
},
)
.await?
.tx_hash;
let updated_value =
read_public_u128(&wallet, address, derive_storage_slot_in_map(2, &owner)).await?;
println!("Contract address: {address}");
println!("Sim return values: {}", sim.return_values);
println!(
"Suggested gas: da={} l2={}",
gas_limits.gas_limits.da_gas, gas_limits.gas_limits.l2_gas
);
println!("Profile payload: {}", profile.profile_data);
println!("Sent tx hash: {tx_hash}");
println!("Updated value: {updated_value}");
Ok(())
}
References
aztec-contractreferenceaztec-walletreference —SendOptions,SimulateOptions,ProfileOptions.- Events guide