An initializer function is where a contract initializes its state.
Initializer functions are similar to constructors:
can only be called once
external non-initializer functions cannot be called until one of the initializers has been called
The only exception are noinitcheck functions, which can be called even if none of the initializers has been
called.
Initialization Commitment
All contract instances have their address include a commitment to one of their initializer functions, along with
parameters and calling address. Any of the following will therefore fail:
calling the wrong initializer function
calling the initializer function with incorrect parameters
calling the initializer function from the incorrect address
It is possible however to allow for any account to call the specified initializer by setting the intended caller to
the zero address. These are called 'universal deployments'.
Multiple Initializers
A contract can have multiple initializer functions, but it is not possible to call multiple initializers on the
same instance: all initializers become disabled once any of them executes. Each individual instance can call any of
the initializers.
Initializers can be either private or public. If a contract needs to initialize both private and public state, then
it should have an external private function marked as initializer which then enqueues a call to an
external public function not marked as initializer and instead marked as only_self (so that it can
only be called in this manner).
Lack of Initializers
If a contract has no initializer function, initialization is then not required and all functions can be called at
any time. Contracts that do have initializers can also make some of their functions available prior to
initialization by marking them with the #[noinitcheck] attribute - though any contract state initialization will
of course not have taken place.
How It Works
Initializers emit nullifiers to mark the contract as initialized. Two separate nullifiers are used (a private
initialization nullifier and a public initialization nullifier) because private nullifiers are committed before
public execution begins: if a single nullifier were used, public functions enqueued by the initializer would see
it as existing before the public initialization code had a chance to run.
The private initialization nullifier is computed from the contract address and the contract's init_hash. This
means that address knowledge alone is insufficient to check whether a contract has been initialized, preventing
a privacy leak for fully private contracts.
Private initializers emit the private initialization nullifier. For contracts that also have external
public functions, they auto-enqueue a call to an auto-generated public function that emits the public
initialization nullifier during public execution. This function name is reserved and cannot be used by
contract developers.
Public initializers emit both nullifiers directly.
Private external functions check the private initialization nullifier.
Public external functions check the public initialization nullifier.
An initializer function is where a contract initializes its state.
Initializer functions are similar to constructors:
externalnon-initializer functions cannot be called until one of the initializers has been calledThe only exception are
noinitcheckfunctions, which can be called even if none of the initializers has been called.Initialization Commitment
All contract instances have their address include a commitment to one of their initializer functions, along with parameters and calling address. Any of the following will therefore fail:
It is possible however to allow for any account to call the specified initializer by setting the intended caller to the zero address. These are called 'universal deployments'.
Multiple Initializers
A contract can have multiple initializer functions, but it is not possible to call multiple initializers on the same instance: all initializers become disabled once any of them executes. Each individual instance can call any of the initializers.
Initializers can be either private or public. If a contract needs to initialize both private and public state, then it should have an
externalprivate function marked asinitializerwhich then enqueues a call to anexternalpublic function not marked asinitializerand instead marked asonly_self(so that it can only be called in this manner).Lack of Initializers
If a contract has no initializer function, initialization is then not required and all functions can be called at any time. Contracts that do have initializers can also make some of their functions available prior to initialization by marking them with the
#[noinitcheck]attribute - though any contract state initialization will of course not have taken place.How It Works
Initializers emit nullifiers to mark the contract as initialized. Two separate nullifiers are used (a private initialization nullifier and a public initialization nullifier) because private nullifiers are committed before public execution begins: if a single nullifier were used, public functions enqueued by the initializer would see it as existing before the public initialization code had a chance to run.
The private initialization nullifier is computed from the contract address and the contract's
init_hash. This means that address knowledge alone is insufficient to check whether a contract has been initialized, preventing a privacy leak for fully private contracts.For private non-initializer functions, the cost of this check is equivalent to a call to
PrivateContext::assert_nullifier_exists. For public ones, it is equivalent to a call toPublicContext::nullifier_exists_unsafe.The
noinitcheckattribute can be used to skip these checks.only_selffunctions also implicitly skip them.