DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Phantom owner – unusable admin functions in `PerpetualVault` contract

Summary

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.

Vulnerability Details

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:

function initialize(
address _market,
address _keeper,
address _treasury,
address _gmxProxy,
address _vaultReader,
uint256 _minDepositAmount,
uint256 _maxDepositAmount,
uint256 _leverage
) external initializer {
__Ownable2Step_init(); //@audit
__ReentrancyGuard_init();
// ... other initialization logic
}

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.

Impact

  • 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.

Tools Used

  • Manual Code Review

  • OpenZeppelin Documentation and Upgradeable Contract Patterns

Recommendations

  • 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:

    function initialize(
    address _market,
    address _keeper,
    address _treasury,
    address _gmxProxy,
    address _vaultReader,
    uint256 _minDepositAmount,
    uint256 _maxDepositAmount,
    uint256 _leverage
    ) external initializer {
    __Ownable_init(); // Set the owner to msg.sender
    __Ownable2Step_init(); // Retain for two-step ownership logic if required
    __ReentrancyGuard_init();
    // ... rest of the initialization logic
    }
Updates

Lead Judging Commences

n0kto Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

Suppositions

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.

n0kto Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

Suppositions

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.

Appeal created

vladislavvankov0 Submitter
3 months ago
riceee Auditor
3 months ago
n0kto Lead Judge
3 months ago
n0kto Lead Judge
3 months ago
n0kto Lead Judge 2 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

Suppositions

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.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.