Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  1. Class registration — publishes the class hash + artifact metadata once per class (idempotent for the same class id).
  2. Instance publication — creates the deterministic instance at an address derived from (class_id, constructor_args, salt, public_keys).
  3. 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(())
}

References