Contract Deployment
In the Aztec protocol, contracts are created as instances of contract classes. The deployment process consists of two main steps: first publishing the contract class (if not already published), and then creating a contract instance that references this class.
Contract Classes
A contract class is a collection of state variable declarations, and related private, public and utility functions. Contract classes don't have state, they just define code (storage structure and function logic). A contract class cannot be called; only a contract instance can be called.
Key Benefits of Contract Classes
Contract classes simplify code reuse by making implementations a first-class citizen in the protocol. With a single class registration, multiple contract instances can be deployed that reference it, reducing deployment costs. Classes also facilitate upgradability by decoupling state from code, making it easier for an instance to switch to different code while retaining its state.
Structure of a Contract Class
A contract class includes:
artifact_hash: Hash of the contract artifactprivate_functions_root: Merkle root of the private functions treepacked_public_bytecode: Packed bytecode representation of the AVM bytecode for all public functions
The specification of the artifact hash is not enforced by the protocol. It should include commitments to utility functions code and compilation metadata. It is intended to be used by clients to verify that an offchain fetched artifact matches a registered class.
Contract Class Registration
A contract class is published by calling a private publish function in a canonical ContractClassRegistry contract, which emits a registration nullifier. This process guarantees that the public bytecode for a contract class is publicly available, which is required for deploying contract instances.
Contract class registration can be skipped if there are no public functions, and the contract will still be usable privately. However, if you have public functions, you must either register the class before deployment or skip public deployment entirely (only private functions will be callable).
Contract Instances
A deployed contract is effectively an instance of a contract class. It always references a contract class, which determines what code it executes when called. A contract instance has both private and public state, as well as an address that serves as its identifier.
Structure of a Contract Instance
A contract instance includes:
salt: User-generated pseudorandom value for uniquenessdeployer: Optional address of the contract deployer. Zero for universal deploymentcontract_class_id: Identifier of the contract class for this instanceinitialization_hash: Hash of the selector and arguments to the constructorpublic_keys: Public keys used for encryption and nullifying (nullifier, incoming viewing, outgoing viewing, and tagging keys)
Instance Address
The address of a contract instance is computed as the hash of the elements in its structure. This computation is deterministic, allowing users to precompute the expected deployment address of their contract, including account contracts.
Contract Initialization vs. Public Deployment
Aztec makes an important distinction between initialization and public deployment:
- Initialization: A contract instance is considered initialized once it emits an initialization nullifier, meaning it can only be initialized once. The default state for any address is uninitialized. A user who knows the preimage of the address can still issue a private call into a function in the contract, as long as that function doesn't assert that the contract has been initialized.
- Public Deployment: A contract instance is considered publicly deployed when it has been broadcast to the network via the
publish_for_public_executionfunction in the canonicalContractInstanceRegistrycontract, which emits a deployment nullifier. All public function calls to an undeployed address fail, since the contract class is not known to the network.
Initialization
Contract constructors are not enshrined in the protocol, but handled at the application circuit level. Constructors are methods used for initializing a contract, either private or public, and contract classes may declare more than a single constructor. They can be declared by the #[initializer] macro. You can read more about how to use them on the defining initializer functions page.
A contract must ensure:
- It is initialized at most once
- It is initialized using the method and arguments defined in its address preimage
- It is initialized by its deployer (if non-zero)
- Functions dependent on initialization cannot be invoked until the contract is initialized
Functions in a contract may skip the initialization check.
Verification of Executed Code
When a function is called on a contract instance, the protocol circuits verify that the executed code matches what was registered. For private functions, the circuit checks that the function's verification key hash exists in the private_functions_root of the contract class. For public functions, the AVM verifies that the bytecode matches the registered packed_public_bytecode. This verification ensures that contracts execute the exact code that was published during class registration.
Genesis Contracts
The ContractInstanceRegistry and ContractClassRegistry contracts are protocol contracts that exist from the genesis of the Aztec Network at predefined addresses. They are necessary for deploying other contracts to the network.
Further reading
To see how to deploy a contract in practice, check out the dapp development tutorial.