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

Account Lifecycle

End-to-end: generate keys, register, deploy the account contract, send the first transaction.

Runnable Example

examples/account_deploy.rs walks through the full lifecycle with the embedded wallet. examples/multiple_accounts_one_encryption_key.rs shows sharing encryption keys across accounts.

Stages

  1. Key generation — derive signing + master keys via aztec-crypto::derive_keys.
  2. Account flavor — pick SchnorrAccount (default) or SignerlessAccount (tests / bootstrap).
  3. Registration — install the account into the PXE so it can decrypt notes (Pxe::register_account).
  4. Deployment — call AccountManager::create(...), then deploy_method().await?, then DeployAccountMethod::send(...), funded by a fee strategy (see Fee Payments).
  5. First transaction — send a call via the newly deployed account’s entrypoint through the wallet.

Sketch

use aztec_rs::account::{AccountManager, DeployAccountOptions, SchnorrAccountContract};
use aztec_rs::wallet::SendOptions;

let secret_key = /* Fr::random() or loaded */;
let contract = SchnorrAccountContract::new(secret_key);

let manager = AccountManager::create(
    wallet.clone(),
    secret_key,
    Box::new(contract),
    None::<aztec_rs::types::Fr>,
)
.await?;
let deploy = manager.deploy_method().await?;
let result = deploy
    .send(
        &DeployAccountOptions::default(),
        SendOptions {
            from: /* sponsor or fee-paying account */,
            ..Default::default()
        },
    )
    .await?;
println!("account deployed at {}", result.instance.address);

Edge Cases

  • Self-paid deployment: the account can’t authorize its own deployment before it exists on-chain; use SignerlessAccount + a claim- or sponsor-based fee strategy for the very first tx.
  • Re-registration is idempotent.
  • Chain re-orgs may rewind the deployment tx; applications SHOULD wait for wait_for_contract before treating the account as usable.

Full Runnable Example

Source: examples/account_deploy.rs.

//! Deploy a fresh Schnorr account and then use it with its own wallet.

#![allow(clippy::print_stdout, clippy::wildcard_imports)]

mod common;

use common::*;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), aztec_rs::Error> {
    let Some((sponsor_wallet, sponsor)) = setup_wallet(TEST_ACCOUNT_0).await else {
        return Err(aztec_rs::Error::InvalidData(format!(
            "node not reachable at {}",
            node_url()
        )));
    };

    let secret = Fr::random();
    let sponsor_wallet = Arc::new(sponsor_wallet);
    let manager = AccountManager::create(
        Arc::clone(&sponsor_wallet),
        secret,
        Box::new(SchnorrAccountContract::new(secret)),
        Some(next_unique_salt()),
    )
    .await?;
    let account_address = manager.address();
    let complete = manager.complete_address().await?;
    let instance = manager.instance().clone();

    let compiled_account = load_schnorr_account_artifact();
    let class_id = instance.inner.current_contract_class_id;
    sponsor_wallet
        .pxe()
        .contract_store()
        .add_artifact(&class_id, &compiled_account)
        .await?;
    sponsor_wallet
        .pxe()
        .contract_store()
        .add_instance(&instance)
        .await?;
    sponsor_wallet
        .pxe()
        .key_store()
        .add_account(&secret)
        .await?;
    sponsor_wallet.pxe().address_store().add(&complete).await?;
    seed_signing_key_note(
        sponsor_wallet.pxe(),
        &SchnorrAccountContract::new(secret),
        account_address,
        2,
    )
    .await;

    let deploy_result = manager
        .deploy_method()
        .await?
        .send(
            &DeployAccountOptions {
                from: Some(sponsor),
                ..Default::default()
            },
            SendOptions {
                from: sponsor,
                additional_scopes: vec![account_address],
                ..Default::default()
            },
        )
        .await?;

    let (account_wallet, _, _, _) =
        setup_registered_schnorr_wallet(secret, complete.clone(), instance, "generated").await?;
    let managed_accounts = account_wallet.get_accounts().await?;
    let pxe_accounts = account_wallet.pxe().get_registered_accounts().await?;
    let auth_wit = account_wallet
        .create_auth_wit(
            account_address,
            MessageHashOrIntent::Hash {
                hash: Fr::from(123u64),
            },
        )
        .await?;

    println!("Sponsor:            {sponsor}");
    println!("Account address:    {}", complete.address);
    println!("Deploy tx hash:     {}", deploy_result.send_result.tx_hash);
    println!(
        "Managed accounts:   {}",
        managed_accounts
            .iter()
            .map(|entry| format!("{}={}", entry.alias, entry.item))
            .collect::<Vec<_>>()
            .join(", ")
    );
    println!("Auth witness hash:  {}", auth_wit.request_hash);
    println!("PXE accounts:       {}", pxe_accounts.len());

    Ok(())
}

References