Starknet contract deployment from a declared class
Updated -Starknet contract deployment creates an instance of a class that already exists on the target network. Declaration registers the reusable code, while deployment assigns an address and runs any constructor with its supplied data. If the network already holds the class, deployment can reuse it without another declaration. The remaining requirements concern the deployment method, address inputs, constructor permissions, and transaction resources. A predicted address identifies a possible location; a successful receipt establishes that execution completed.
An existing class changes the starting point
Class availability on the intended network decides whether the next operation is declaration or instance deployment. A successful
DECLARE
transaction adds a class to that network's state. An existing class can supply multiple instances, each with separate storage. A class hash from another network does not establish that the class exists locally. For an ordinary application contract, a signed account invocation can call a deployment factory. The factory invokes the deployment system call; the Universal Deployer Contract (UDC) provides a standard implementation.
What does a class hash identify?
A class hash identifies a reusable contract class, while instance addresses identify the contracts that execute it with separate storage.
Sierra and compiled code
For Sierra-based classes, compilation turns Cairo source into Sierra, an intermediate program representation. The declaration also commits a compiled class hash for the corresponding Cairo assembly, commonly called CASM. These hashes identify different artifacts, so instance deployment takes the class hash as its code identifier.
Compiler compatibility matters before the class reaches the network, because the target protocol supports particular Sierra versions and compilation rules. A newer local compiler does not automatically produce an artifact that an older network accepts. Keep the Sierra artifact, its matching compiled output, and the build configuration together. Mixing outputs from separate builds can break the relationship that declaration checks.
The ABI and deployment arguments
The application binary interface (ABI) describes callable functions and their argument types. Tools use it to encode constructor inputs and decode responses, and its contents also contribute to a Sierra class hash. An ABI file belongs with its corresponding artifact, because an unrelated file can describe a different argument layout. Declaration does not enforce that the supplied ABI accurately describes the underlying program. A matching class hash alone cannot resolve an incorrect interface.
The declaration transaction hash tracks an operation, the class hash identifies code, and the contract address identifies an instance.
Constructor data sets storage and permissions
A constructor initializes a new instance during deployment, so its arguments can determine both starting storage and control permissions. Separate instances can share code while holding different initial values, and constructor checks can reject correctly encoded inputs that violate the contract's rules.
Argument encoding follows the declared interface
Cairo serializes constructor arguments into
felt252
field elements. Argument order and type layout must match the constructor; a
u256
occupies two elements, with its low part before its high part. Arrays include their length before their serialized members. These rules explain why a constructor's argument count can differ from its calldata length. An ABI-aware encoder applies these layouts to typed arguments.
The constructor sees the immediate deployer
With the Universal Deployer Contract, a constructor's
get_caller_address
call returns the UDC address.
The signing account remains a separate actor, so a constructor that takes its owner from the immediate caller would assign control to the factory. A constructor that accepts an explicit owner argument can initialize ownership from that value, with its implementation determining which approach applies. Account abstraction governs how the account authorizes the invocation; it does not replace the constructor's ownership logic.
Constructor inputs enter transaction calldata. Public deployments expose those values, so passwords and private keys have no place in constructor arguments.
Factory choice determines the address context
The address calculation combines the class hash, salt, constructor calldata, and deployer context. Changing these inputs can change the address even when the source code stays the same.
The deployment system call
A contract can call
deploy_syscall
directly when its implementation exposes that behavior. The system call takes the class hash, an address salt, constructor calldata, and
deploy_from_zero. That flag selects whether address calculation uses zero or the calling contract's address as the deployer component. It changes address derivation; it does not change which contract calls the constructor. A custom factory can therefore affect both addressing and initialization.
UDC address modes
The OpenZeppelin UDC interface with
not_from_zero
supports origin-dependent and origin-independent addresses. With the flag set to true, it mixes the caller's address into the salt, reserving address space for that caller. Origin-independent deployment uses the class hash, salt, and constructor arguments without that origin dependence. Matching inputs can reproduce an address across networks when the same mode applies. Each network still needs the class in its own state before a calculated address can contain its instance.
Address prediction must follow the factory's exact salt transformation and deployment mode. A generic calculation can miss those factory-specific inputs.
Why does the deployment fee change?
Deployment fees change with transaction resources and network gas prices, including the work that the constructor performs. From protocol version 0.14.0, version 3 transactions bound L1 gas, L1 data gas, and L2 gas. Each bound includes a maximum resource amount and price per unit. Each maximum unit price must cover the corresponding block price for inclusion, while bounds constrain the allowed payment. Estimate declaration and deployment separately when both operations are necessary. A saved estimate can become stale as prices or contract state change. Ordinary version 3 transaction fees use STRK.
Refreshing a fee estimate without redeclaring the class
Consider a hypothetical deployment of a class with a constructor, using a selected class hash, constructor data, salt, funded account, and target network. The network already holds the class, and the predicted address has no instance. A saved resource price bound falls below the corresponding recent block price, and the client has not submitted the deployment.
A class lookup settles whether this network needs declaration before deployment or already holds the intended class. Here, the lookup returns the intended class. The saved bound needs review before submission; declaring the same code again would not fix its price mismatch.
The deployment client refreshes its fee estimate for the unchanged deployment call. After comparing the revised limits with the account balance, the client updates the transaction's resource bounds. This preserves the class, constructor data, salt, and predicted address. Resource bounds constrain inclusion and payment without entering the contract's address calculation.
In the completed example, the revised transaction reaches a block and its receipt reports
SUCCEEDED
with
ACCEPTED_ON_L2. A class-hash query at that address returns the intended class. The constructor initializes the new instance, using the same declared class despite the changed resource price.
What confirms that the instance exists?
A successful deployment receipt and the resulting contract state confirm the instance; the returned transaction hash only identifies the submitted operation. Inspect execution status separately from finality status. An
INVOKE
transaction can enter a block with
REVERTED
execution, so block inclusion alone cannot establish successful deployment.
ACCEPTED_ON_L2
identifies consensus-finalized block inclusion.
ACCEPTED_ON_L1
records later Ethereum settlement of the relevant state. Client waiting settings do not change those network states.
The
starknet_getClassHashAt
method reports the class hash at an address for a selected block. Querying a block before deployment can produce an absence result even when a later block contains the instance. Read the constructor's intended storage effects through the contract's available interface as well. A matching class hash confirms the code identity at that block; it does not describe every initialized value or permission.
For a UDC deployment, match the event's emitting contract to the factory before interpreting its address and constructor data. The UDC names its deployment event
ContractDeployed.
Starknet: frequently asked questions
Does a single deployment command always mean one transaction?
A single command can submit both declaration and deployment transactions. Starknet Foundry's sncast deploy command can declare first when its --contract-name option selects a contract from a local Scarb project whose class is absent from the network. Deploying an existing class by its class hash skips that declaration. If the command submits both transactions, each has its own fee limits and receipt.
Is deploying a new instance enough to update an existing contract?
Deploying another instance does not update the existing contract's address or storage. An upgradeable contract can expose authorized logic that replaces its class through replace_class. The replacement class must be declared. Its storage behavior must remain compatible to preserve existing data. Deploying new code elsewhere does not grant authority to perform an upgrade.
What happens to the account nonce if a constructor fails?
A deployment invocation that enters a block as REVERTED still advances the sending account's nonce. Execution changes roll back, including the failed instance deployment, while the network can charge for consumed resources. A local encoding error or rejected submission is a different state and does not by itself establish an onchain nonce change.
Can someone else deploy an instance of a class that another account declared?
Declaring a class does not reserve every future instance for the declaring account. Other contracts can instantiate an available class through supported deployment calls. The constructor or a particular factory may impose its own permission checks. Declaration identifies available code; those checks determine whether a particular deployment succeeds.
Why can a client timeout leave the deployment outcome uncertain?
A client timeout ends the local wait without establishing the network's final result. The submitted transaction may continue processing afterward. Early received-transaction information can also depend on the full node that handled submission. A timeout message alone therefore proves neither successful deployment nor a reverted transaction.
Will declaration publish the original Cairo project files?
Declaration publishes the contract class, which contains the intermediate program, entry points, and ABI material for a Sierra-based contract. Original Cairo source files, dependency configuration, and other project files remain separate artifacts. Retrieving the class therefore does not reconstruct the complete source project.