-
Notifications
You must be signed in to change notification settings - Fork 271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect issues in parent initializer calls #1095
base: master
Are you sure you want to change the base?
Conversation
(fnDef.modifiers.some(modifier => | ||
['initializer', 'reinitializer', 'onlyInitializing'].includes(modifier.modifierName.name), | ||
) || | ||
['initialize', 'initializer', 'reinitialize', 'reinitializer'].includes(fnDef.name)) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For another PR, I think it may be useful if we annotate initializers with @custom:initializer
or similar.
The idea is that there are contracts with initializers that don't follow the initialize
or initializer
convention in its name.
A concrete example is any of the Signers we created for ERC4337 accounts:
abstract contract SignerRSA is AbstractSigner {
error SignerRSAUninitializedSigner(bytes e, bytes n);
bytes private _e;
bytes private _n;
/// @custom:initializer true
function _initializeSigner(bytes memory e, bytes memory n) internal {
if (_e.length != 0 || _n.length != 0) revert SignerRSAUninitializedSigner(e, n);
_e = e;
_n = n;
}
...
}
The previous approach did not work for one of the main use cases that we want to detect: missing initializer calls to transitive parent initializers. Here is an illustrating example:
Ownable2StepUpgradeable inherits OwnableUpgradeable but does not call its initializer, the user's contract must call it.The previous approach would not detect this issue because it only checks the direct parent initializers. Instead, I changed this to get the entire linearized base contracts list, remove the ones that are already initialized by some other parent in the list, and then check if the user's contract has an initializer that calls the remaining ones. Remaining considerations:
|
Detects issues in parent initializer calls.
This infers functions as initializers if they have a modifier that is
initializer
,reinitializer
, oronlyInitializing
, or the function itself is named asinitialize
,initializer
,reinitialize
, orreinitializer
.For parent contracts, we only consider
internal
functions as initializers that must be called from the child.For child contracts, we only consider non-
private
functions as initializers.This reports an error if any parent contract has an inferred initializer and any of the following are true for the (child) contract being validated:
Limitations (could be future improvements):
missing-initializer-call
public
initializer (which is ignored in this PR), the child contract does not strictly need its own initializer if the child contract has nothing else to initialize. But if the child contract DOES have an initializer, should we enforce that it also calls the parent'spublic
initializer?Fixes #160