Deploying Contracts
Deploy a compiled Noir / Aztec contract via the builder API in aztec-contract.
Runnable Examples
examples/deploy_contract.rs— minimal deployment.examples/deploy_options.rs— all builder options.examples/account_deploy.rs— deploying an account contract.examples/contract_update.rs— updating a contract class.
Typical Flow
use aztec_rs::abi::{AbiValue, ContractArtifact};
use aztec_rs::deployment::{ContractDeployer, DeployOptions};
use aztec_rs::wallet::SendOptions;
let artifact: ContractArtifact = /* load_artifact(...) */;
let deployer = ContractDeployer::new(artifact, &wallet)
.with_constructor_name("constructor")
.with_public_keys(public_keys);
let method = deployer.deploy(vec![
AbiValue::Field(initial_supply.into()),
])?;
let result = method
.send(&DeployOptions::default(), SendOptions::default())
.await?;
println!("deployed at: {}", result.instance.address);
What the Deployer Does
- Class registration — publishes the class hash + artifact metadata once per class (idempotent for the same class id).
- Instance publication — creates the deterministic instance at an address derived from
(class_id, constructor_args, salt, public_keys). - Constructor call — executes the initializer function inside the deployment tx.
Deterministic Addresses
use aztec_rs::deployment::{
ContractInstantiationParams, get_contract_instance_from_instantiation_params,
};
let params = ContractInstantiationParams { /* ... */ };
let instance = get_contract_instance_from_instantiation_params(&artifact, params)?;
let expected_address = instance.address;
Use this to predict an address before sending the deploy tx (useful for authwits that reference the deployed contract).
Low-Level Split
publish_contract_class(wallet, &artifact).await? and publish_instance(wallet, &instance)? build the low-level interactions for class and instance publication separately when the class is shared across many deploys.
Edge Cases
- Re-deploying with identical
(salt, args)yields the same address; the deployer recognises this and returns the existing instance rather than erroring. - Deploying through an account that does not yet have Fee Juice requires a non-native fee strategy — see Fee Payments.
Full Runnable Example
Source: examples/deploy_contract.rs.
//! Deploy a contract against the local network, then verify wallet and node state.
#![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 artifact = load_stateful_test_artifact();
let deploy = Contract::deploy(
&wallet,
artifact.clone(),
vec![abi_address(owner), AbiValue::Field(Fr::from(42u64))],
None,
)?;
let deploy_result = deploy
.send(
&DeployOptions {
contract_address_salt: Some(next_unique_salt()),
..Default::default()
},
SendOptions {
from: owner,
..Default::default()
},
)
.await?;
let address = deploy_result.instance.address;
let class_id = deploy_result.instance.inner.current_contract_class_id;
let contract_meta = wallet.get_contract_metadata(address).await?;
let class_meta = wallet.get_contract_class_metadata(class_id).await?;
let initial_sum = call_utility_u64(
&wallet,
&artifact,
address,
"summed_values",
vec![abi_address(owner)],
owner,
)
.await?;
let tx_hash = send_call(
&wallet,
build_call(
&artifact,
address,
"increment_public_value",
vec![abi_address(owner), AbiValue::Integer(84)],
),
owner,
)
.await?;
let public_value =
read_public_u128(&wallet, address, derive_storage_slot_in_map(2, &owner)).await?;
println!("Contract address: {address}");
println!("Deploy tx hash: {tx_hash}");
println!("Class ID: {class_id}");
println!("Initial sum: {initial_sum}");
println!("Public value: {public_value}");
println!("Class registered: {}", class_meta.is_artifact_registered);
println!(
"Class published: {}",
class_meta.is_contract_class_publicly_registered
);
println!(
"Instance initialized:{}",
contract_meta.is_contract_initialized
);
Ok(())
}