The PerpetualVault contract fails to properly initialize the ownership mechanism. Although it calls __Ownable2Step_init()
, it never calls __Ownable_init()
from OpenZeppelin’s OwnableUpgradeable. This omission results in the owner not being set during initialization, leaving the contract without proper administrative control.
The PerpetualVault contract is designed as an upgradeable contract and inherits from Ownable2StepUpgradeable
, which itself extends OwnableUpgradeable
. In the upgradeable contract pattern, constructors are replaced with initializer functions. Typically, __Ownable_init()
is required to set the owner (usually to msg.sender
). However, the current initialization sequence in PerpetualVault is as follows:
Here, __Ownable2Step_init()
is called, but this function is empty and does not include a call to __Ownable_init()
. As a result, the owner is never set and remains at the default value of address(0)
.
The only functions that require ownership in this contract are those inherited from the Ownable modules – primarily the administrative functions such as:
transferOwnership(address newOwner)
– Initiates a two-step ownership transfer process by setting a pending owner.
acceptOwnership()
– Allows the pending owner to accept ownership.
Because these functions rely on the correct initialization of the owner, their behavior becomes unpredictable when the owner remains unset.
Inaccessibility of Administrative Functions:
With the owner unset (i.e., remaining as address(0)
), functions protected by the onlyOwner
modifier—specifically transferOwnership
—cannot be executed by any legitimate administrator. Since onlyOwner
checks that the caller matches the stored owner (which is address(0)
), no external account can satisfy this condition.
Permanent Loss of Administrative Control:
Because the onlyOwner functions (e.g., initiating a transfer of ownership) are rendered unusable, any future attempts to update administrative settings or transfer control will fail. This locks the contract into its current configuration, potentially preventing critical upgrades or recovery actions.
Manual Code Review
OpenZeppelin Documentation and Upgradeable Contract Patterns
Update the Initialization Sequence:
Modify the initialize()
function in the PerpetualVault contract to include a call to __Ownable_init()
. This call is necessary to properly set the owner .
Example Update:
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.