The Solidity Constructor: Runs Once, Then Never Again
What a constructor does, why it is the only function that cannot be called twice, how immutable and constant differ from it, and what it means when a contract has none.
TL;DR
- A constructor runs exactly once, during deployment, and then never again. There is no way to call it afterwards, by anyone, ever.
- It is where a contract's starting state gets written: who owns it, what its parameters are, which other contracts it points at.
- Because it runs at deployment,
msg.senderinside a constructor is the deployer, which is the standard way a contract learns who its owner is. - A contract with no constructor is legal and common. It simply starts with every state variable at its zero value.
- Constructor code is not part of the deployed contract. It runs, sets state, and is discarded.
What is a constructor in Solidity?
A constructor is a special function that the chain runs a single time, while the contract is being deployed, to set up its starting state, and which can never be called again once deployment finishes.
Here it is in OpenZeppelin's Ownable, which thousands of deployed contracts inherit from:
abstract contract Ownable is Context {
address private _owner;
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
Four lines of body, and they do the entire setup. It refuses the zero address outright, then records whoever the deployer named as the owner.
Within the wider method of how to read a smart contract, the constructor is the first function you read after the state variables, because it is where those values came from. Call it the birth certificate. It is written at the moment the thing comes into existence, it records facts about that moment, and it is never rewritten. Note what it does not do: it never reads msg.sender to decide the owner. The deployer has to say who it is, which means the deploying account and the owning account need not be the same.
Why does a constructor only run once?
Because it is not part of the deployed contract at all.
Deploying a contract means sending a transaction whose payload is the contract's creation code. The chain executes that creation code, the constructor runs as part of it, state gets written, and then the chain stores the runtime code at the new address. The constructor is in the creation code, not the runtime code. Once deployment finishes, there is physically nothing left to call.
This is why "can I call the constructor again?" has a harder no as its answer than most access questions in Solidity. It is not that a check refuses you. There is no function there.
One practical consequence for readers: the constructor is the only place in a contract where you will see setup logic that has no access control on it and does not need any. Nobody else was ever going to get the chance.
What is msg.sender inside a constructor?
The deployer. This is the detail that makes the whole pattern work.
msg.sender always means whoever is calling right now, in any function, at any moment. During deployment, the caller is the account that sent the deployment transaction. So this line:
_transferOwnership(initialOwner);
hands the named address to an internal function that writes it into _owner and announces the change. If Ana deploys and names herself, Ana is the owner from that instant. Unlike many contracts, Ownable does let ownership change later, through transferOwnership, so the constructor sets the first owner rather than the only one.
That is the reading move worth internalising. To know whether a contract's owner can change, do not reason about it. Search the file for every assignment to the variable. In Ownable, every write to _owner happens inside _transferOwnership, which is reachable from the constructor, from transferOwnership, and from renounceOwnership. Three doors, so the answer is yes, it can change. More on this in state variables in Solidity and msg.sender explained.
What if a contract has no constructor?
That is legal, common, and means something specific: every state variable starts at its zero value.
Solidity has no concept of an uninitialised variable. Every type has a default:
| Type | Default |
|---|---|
uint256 and other numbers | 0 |
bool | false |
address | address(0), the zero address |
string and bytes | empty |
So a contract with no constructor deploys with all numbers at zero, all flags false, and every address slot holding the zero address. That is not an error state, it is the defined starting point, and plenty of contracts are designed to start exactly there.
It does mean a specific question is always worth asking when you see no constructor: is there another function that performs setup instead? Upgradeable contracts in particular cannot use constructors in the usual way, and use an initialize function instead. That function is ordinary code, callable like any other, and whether it is protected from being called a second time is a real question you have to answer by reading it. A constructor never needed that protection. An initialiser does.
How do constant and immutable relate to this?
Both are ways of fixing a value, and the difference is when it gets fixed.
contract Example {
uint256 public constant MAX = 100; // fixed when compiled
address public immutable deployer; // fixed when deployed
constructor() {
deployer = msg.sender; // the only place immutable can be set
}
}
A constant is baked in at compile time, so it must be a literal value the compiler already knows. A value marked immutable is set once during deployment, which means the constructor is the only place it can be assigned, and then it is fixed forever.
Both are cheaper to read than ordinary state because neither lives in storage. And both give a reader a strong guarantee for free: a value declared constant or immutable cannot change, and you do not need to search the file to confirm it. The keyword is the proof.
Can a constructor take arguments?
Yes, and that is how the same contract source becomes many differently configured deployments.
constructor(address initialOwner) {
initialOwner is a parameter. Whoever deploys the contract supplies its value in the deployment transaction, and different deployers can supply different values. One Ownable source, and every contract inheriting from it starts life owned by whichever address its deployer named.
The leading underscore is a naming convention, not a language feature. It is widely used to distinguish a parameter from the state variable it will be assigned to, so _goal the input does not get confused with goal the stored value.
For a reader looking at a deployed contract, constructor arguments are worth chasing down, because they are the configuration that makes this instance different from every other instance of the same code. Block explorers show them, which is covered in how to read a contract on Etherscan.
Delete the word and ask what breaks
- Delete the
_transferOwnership(initialOwner);line and the owner stays at the zero address forever, so everyonlyOwnerfunction becomes permanently uncallable. - Rename
constructortofunction setup()and it stops being special. It becomes an ordinary function anyone can call, at any time, as often as they like, and whoever calls it first decides who owns the contract. - Delete the whole constructor and the contract deploys with an owner of zero, which is exactly the state the zero-address check exists to prevent.
- Delete the zero-address check and a careless deployment can create a contract nobody owns and nobody can ever administer.
Related questions
Can you call a constructor after deployment? No. The constructor exists only in the contract's creation code, which runs during deployment and is not stored at the contract's address. After deployment there is no function to call.
What happens if a contract has no constructor? It deploys successfully and every state variable holds its type's zero value: numbers at zero, booleans false, addresses at the zero address. Many contracts are written this way deliberately.
Why do upgradeable contracts use initialize instead of a constructor? Because their state lives in a separate proxy contract, so constructor code running against the implementation would write state in the wrong place. They use an ordinary function instead, which means protecting it from being run twice becomes the author's responsibility.
What is the difference between constant and immutable?
constant is fixed at compile time and must be a literal the compiler can evaluate. immutable is fixed at deployment and can be assigned in the constructor, which lets it depend on deployment-time values such as msg.sender.
Can a constructor be payable?
Yes. Marking a constructor payable lets the deployment transaction send ETH along with it, so the contract can start life already holding a balance.
Why do parameters often start with an underscore? Purely convention, to avoid confusing a parameter with the state variable of the same name that it is about to be assigned to. Solidity does not treat the underscore specially in this position.
Where to go next
The constructor is the shortest and most consequential part of most contracts. It runs once, it sets the facts everything else depends on, and it is gone. When you open an unfamiliar contract, reading the constructor immediately after the state variables tells you where those values came from and, just as often, that they can never change.
The word doing the quiet work in that constructor is msg.sender, and it shows up in nearly every contract you will ever read: msg.sender in Solidity.
Tagged