Risks in initializing Proxy contracts?
Using an `initialize` function in proxy contracts can introduce risks if its not implemented correctly
Please note that this artcile mostly applies to proxy patterns provided by OpenZeppelin.
- One risk is that if the implementation contract doesn't define an
initialize
function, or if theinitialize
function has a different signature or behavior in the implementation contract compared to the proxy contract, then the initialization process can fail or behave unexpectedly. - Prevent the
initialize
function being called more than once. If the initialize function has not implemented a check to prevent if it has already been called, then it can be called multiple times, potentially leading to unexpected behavior or security vulnerabilities. For example, if theinitialize
function sets the owner of the contract, calling it multiple times could result in a different owner being set than intended. - When defining an
initialize
function for a proxy contract, developer should need to manually handle the calling of theinitialize
functions of all the inherited contracts. - Avoid assigning initial values to non constant variables in the implementaiton contract. As implmentation contract uses storage of proxy contract, the initial value (or storage) of the variables in implmentation contract would not be available when the contract is called via proxy contract. Hence, it is recommended to initialize the non constant variables in an
initialize
function. - Ensure to initialize the implementation contract in a proxy contract, as leaving it uninitialized can result in a security breach by attackers and negatively impact the proxy. Developers can prevent the use of an uninitialized implementation contract by calling the
_disableInitializers
function in the constructor (as provided by OpenZeppelin library), which automatically locks the implementation contract upon deployment. - A note when using
OpenZeppelin upgrades
, if one creates a new contract instance within the Solidity code (contract clones using factory patterns etc.), it won't be handled by OpenZeppelin Upgrades, which implies that these contracts would not be upgradeable. As cloned contracts will be directly created by solidity code and hence one may have to write additional code to properly initalize these auto deployed contracts.
To mitigate these risks, it's important to carefully design and test the initialization process for proxy contracts. One can use OpenZeppelin hardhat package provided specifically for this purpose, but additional testing is still recommended.
Reference
- https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable